2011-09-09 30 views
5

Tôi đã không hài lòng với các Cominator bằng JavaScript và được tự hào về (hy vọng) khiến S hoạt động khi tôi tình cờ gặp Wikipedia nói: "Bộ kết hợp Y có thể được thể hiện trong SKI- calculus như: Y = S (K (SII)) (S (S (KS) K) (K (SII)))", vì vậy tôi đã phải cố gắng rằng:Biểu thị Y theo thuật ngữ SKI-Combinators trong JavaScript

var I = function (x) { 
      return x; 
     }; 

var K = function (x) { 
     return function(){ 
      return x;} 
     }; 

var S = function (x) { 
      return function (y) { 
       return function (z) { 
        return x(z)(y(z)); 
       } 
      } 
     }; 

var Y = S (K(S(I)(I))) (S(S(K(S))(K)) (K(S(I)(I)))); 

Y; //evals to: 
//function (z) {return x(z)(y(z));} 

//And this (lifted from Crockford's Site): 
var factorial = Y(function (fac) { 
    return function (n) { 
     return n <= 2 ? n : n * fac(n - 1); 
    }; 
}); //fails: 
//RangeError: Maximum call stack size exceeded 

tôi đang làm gì sai? Tôi không dịch chính xác biểu thức đó? Có điều gì sai trái với việc tôi sẽ làm thế nào không? Nó thậm chí có ý nghĩa không? Hầu hết những gì cần đọc về những thứ như thế này chỉ khiến não tôi muốn nổ tung, nên điểm của bài tập này đối với tôi chủ yếu là xem tôi có hiểu được ký hiệu không (và do đó có thể dịch nó sang JavaScript).

Ồ, và, nhân tiện: điều gì khiến tôi đọc lại & một lần nữa là những gì prototype.js thực hiện như Prototype.K thực sự là bộ phối hợp I. Có ai để ý không?

+0

Hah. 1 để làm cho Firefox của tôi nói "quá nhiều đệ quy". –

Trả lời

6

Vấn đề ở đây là bạn đang sử dụng ngôn ngữ lập trình được đánh giá nghiêm ngặt. Bộ kết hợp Y, khá giống với bất kỳ bộ kết hợp điểm cố định nào khác, sẽ chỉ hoạt động đúng khi chức năng của bạn được gọi theo nhu cầu, hoặc 'đánh giá uể oải'.

Tôi biết cách làm việc này (one of my professors looked into it a while ago), nhưng nó sẽ làm cho mã của bạn hoàn toàn không thể đọc được.

Dưới đây tôi đã trình bày chính xác những gì đang diễn ra, hy vọng bạn có thể thấy tại sao JavaScript không thể xử lý đơn giản tính toán SKI.


Đây là những gì Y trông giống như sau Javascript đánh giá của bạn SKI-biểu:

var Y = function (q) { 
    return (function(p){return q(p(p))})(function(p){return q(p(p))}); 
}; 

Bây giờ chúng ta hãy xem những gì sẽ xảy ra nếu bạn ăn nó chức năng của bạn function (fac) { ... }. Hãy gọi đó là chức năng f:

var factorial = (function(p){return f(p(p))})(function(p){return f(p(p))}); 

Kể từ khi chức năng ẩn danh đầu tiên được áp dụng cho một cuộc tranh cãi, nó sẽ được đánh giá thành này:

var factorial = f(
    (function(p){return f(p(p))})(function(p){return f(p(p))}) 
); 

Trong một ngôn ngữ uể oải đánh giá, lập luận để f mà ngày nay bị bỏ lại một mình và bản thân số f sẽ được đánh giá. Tuy nhiên, vì JavaScript là một ngôn ngữ được đánh giá đúng (hoặc 'gọi theo giá trị'), nó muốn biết đối số nào cần phải chuyển đến hàm f trước khi thực sự chạy hàm đó. Vì vậy, chúng ta hãy đánh giá lập luận đó, phải không?

var factorial = f(f(
     (function(p){return f(p(p))})(function(p){return f(p(p))}) 
    ) 
); 

Tôi đoán bây giờ bạn đang bắt đầu thấy bây giờ mọi thứ xảy ra sai và cách bộ kết hợp Y thực sự hoạt động. Trong mọi trường hợp, máy JavaScript của bạn sẽ hết dung lượng ngăn xếp, bởi vì nó đang cố gắng tạo một chồng các cuộc gọi vô hạn tới f.

+0

Cảm ơn câu trả lời tuyệt vời & khai sáng :) –

Các vấn đề liên quan