1

由於是一個簡單的,數學咖喱功能減去的數字:在Javascript中,哪些參數順序與運算符一起作爲預先熟悉的函數和組合?

function sub(x) { 
    return function (y) { 
    return x - y; 
    }; 
}; 

sub(3)(2); // 1 

函數簽名完全相同讀作得到的結果。的情況,只要功能組合物涉及改變:

function comp(f) { 
    return function (g) { 
    return function (x) { 
     return f(g(x)); 
    }; 
    }; 
}; 

function gte(x) { 
    return function (y) { 
    return x >= y; 
    }; 
}; 

comp(gte(2))(sub(3)) (4); // true 

隨着功能的組合物,所涉及的每個函數的最後一個參數是至關重要的,因爲它是與由先前施加的函數返回的值分別進料。上述例子中的組合物因此讀作:4 - 3 >= 2,其將產生false。實際上,構圖背後的計算是:2 >= 3 - 4,其產生true

我可以重寫subgte容易得到期望的結果:

function sub(y) { 
    return function (x) { 
    return x - y; 
    }; 
}; 

function gte(y) { 
    return function (x) { 
    return x >= y; 
    }; 
}; 

comp(gte(2))(sub(3)) (4); // false 

但現在的直接調用函數的返回值是不同的比預期:

sub(3)(2); // -1 (but reads like 1) 
gte(2)(3); // true (but reads like false) 

我可以切換的參數對於每個呼叫或爲每種情況定義部分應用功能:

function flip(f) { 
    return function (x) { 
    return function (y) { 
     return f(y)(x); 
    }; 
    }; 
} 

flip(gte)(2)(3); // false 

var gteFlipped = flip(gte); 
gteFlipped(2)(3); // false 

這兩種變體顯然很麻煩,也不可讀。

哪個參數順序更可取?還是有兩種機制可以使用,這取決於各自的要求(如Haskell的部分應用運算符的左/右部分)?

一個可能的解決方案必須考慮到,我只使用一元函數!

+0

也許你正在尋找:https:// github。com/algesten/fnuc –

+0

@AaditMShah:我知道這個問題,但它指的是具有外部咖喱功能的正常的n元Javascript函數。 – rand

+0

如果你覺得你的問題沒有回答,那麼你可以投票重新提出問題。在此之前,我會考慮你的問題並嘗試找到解決方案。 –

回答

0

此回覆基於Aadit的回覆。

其實是有需要的Javascript完全應用咖喱操作功能 - 作爲一等公民使用時:

function between(ops) { 
    return function (left) { 
    return function (right) { 
     return function (n) { 
     // At this point one should use the native Javascript operators 
     // but they can't be passed to the function, since operators are not First Class. 
     return ops[0](left)(n) && ops[1](right)(n); 
     }; 
    }; 
    }; 
} 

function lte(y) { return function (x) { return x <= y; }; } 
function gt(y) { return function (x) { return x > y; }; } 

between([gt, lte])(2)(4)(4); // true 
// is evaluated as: gt(2)(4) && lte(4)(4) === true; (confusing) 

between可能是廢話,但它可以作爲證明完全應用咖喱操作功能意義在Javascript中。甚至可能還有其他用例。

儘管如此,Aadit是正確的,像sub(2)(3)東西違背了currying的目的!

那麼,解決方案如何看起來像?

  1. 所有咖喱操作功能必須有一個從右到左的參數順序
  2. 功能的引入,其中明確表示不尋常的用法將所有參數傳遞時,一個咖喱功能一次

這裏是uncurryOp

// intended for all operator functions 
function uncurryOp(f) { 
    return function (x, y) { 
    return f(y)(x); 
    }; 
} 

uncurryOp(gt)(2, 4); // false (intuitive) 

這是不是真的有足夠soluti上。我認爲沒有,因爲在Javascript中缺乏頭等艙和部分適用的運營商。

+0

您的'uncurryOp'功能與'flip'相同。 –

+0

@AaditMShah不完全。 'uncurryOp'就像'flip'結合'uncurry'。我認爲'uncurryOp(gt)(2,4)'比'flip(gt)(2)(4)'更具可讀性。但那只是我的個人意見。 – rand

+0

我寫了一個自動翻轉的'flip'函數,可以讓你寫'flip(gt,2,4)'。請參閱以下答案:http://stackoverflow.com/a/27996545/783743 –

0

以下是我讀你的組成:

comp(gte(2))(sub(3)) (4); 

gte(2) = function(y) { return 2 >= y; } // (x = 2) 
sub(3) = function(y) { return 3 - y; } // (x = 3) 

// Therefore: 
comp(gte(2))(sub(3)) = function(x) { 
    var f = function(y) { return 2 >= y; }; 
    var g = function(y) { return 3 - y; }; 
    return f(g(x)); 
}; 

// Now call with (x = 4): 
x = 4 
g(4) = 3 - 4 = -1 
f(-1) = (2 >= -1) = true 

因此,在短期,似乎你的期望是錯誤的。也許你確實有倒退,但我真的不知道是什麼。不過,我確實認爲這不是一種用JavaScript工作的好方法,而且你過於複雜,但這只是我的看法。

+0

所以,你的意思是我的誤解已經在我的斷言中,'comp(gte(2))(sub(3))(4);'讀作'4 - 3> = 2'? – rand

+0

是的。它看起來最終是'2> = 3 - 4',你會注意到,你的操作數和函數的順序都是一樣的。 –

+0

我知道這種風格對於Javascript來說很不尋常,但我相信它是最準確的,如果你更喜歡函數式編程。 – rand

0

所以,你要申請部分運營商,而無需編寫代碼,如:

var gte2 = function (x) { return x >= 2; }; 

這是一個合理的使用情況,「到部分應用運營商」

答案很簡單。只需編寫一個curried函數。例如:

// var gte = y => x => x >= y; // ES6 syntax 

var gte = function (y) { 
    return function (x) { 
     return x >= y; 
    }; 
}; 

var gte2 = gte(2); 

不幸的是,有兩種方法可以部分地適用二元運算符:

  1. 操作部分適用於左參數。
  2. 將運算符部分應用於正確的參數。

這就提出了兩個重要的問題:

  1. 哪些參數應該操作被默認部分應用到?
  2. 我們如何將運算符部分應用於其他參數?

幸運的是,我們可以同意一件事:向操作員提供兩個參數都沒有意義。

// Why write the following: 

add(2)(3) 

// When you can write the following: 

2 + 3 

我們首先創建curried操作符函數的原因是部分應用。

因此,一次提供函數「的參數是無意義的」

這個限制的後果是什麼?這意味着:

  1. 我們可以選擇任何我們想要的參數順序。

    // This is correct: 
    
    var sub = x => y => x - y; 
    
    // So is this: 
    
    var sub = y => x => x - y; 
    
  2. 它只需要給出一個參數的意義。

    // Consider this operator function: 
    
    var sub = y => x => x - y; 
    
    // This makes sense: 
    
    sub(1) // means (x => x - 1) 
    
    // However, this doesn't make sense: 
    
    sub(2)(3) // expected (2 - 3) but actually (3 - 2) 
    
    // But that's OK because it only needs to make sense given one argument. 
    

現在,牢記這一點是最好的參數順序?這得看情況。

  1. 對於commutative操作,參數順序無關緊要。

    例如,加法和乘法都是可交換的。因此,a + b = b + a增加和a * b = b * a乘法。

  2. 對於非交換操作,從右到左的自變量順序通常更好,因爲它允許大聲讀出部分應用程序。

    例如,表達式lt(2)通常表示x => x < 2而不是x => 2 < x

    爲什麼這是一般情況?那麼,JavaScript函數名稱就會出現在參數之前。因此,name(arg)自然讀取爲x => x name arg而不是x => arg name x

  3. 但是,第二條規則有一些例外。最突出的是,師:

    div(10) // is read out loud as divide 10 by x 
         // it is not read out loud as divide x by 10 
    

    當然,對於這樣的邊緣情況下,正確的說法順序是一個見仁見智的問題,但在我的愚見左到右參數順序似乎更自然。

因此,這裏有一堆咖喱操作功能:

// Commutative operators: 

var add = x => y => x + y; 
var mul = x => y => x * y; 

// Right-to-left operators: 

var lt = y => x => x < y; 
var gt = y => x => x > y; 
var lte = y => x => x <= y; 
var gte = y => x => x >= y; 
var sub = y => x => x - y; 

// Left-to-right operators: 

var div = x => y => x/y; 

現在,第二個問題是我們如何部分運營商應用到「其他」說法?

這樣做的唯一方法就是創建一個帶有翻轉參數順序的新函數。

幸運的是,我們並不需要爲每個操作創建一個新的功能:

  1. 對於可交換操作參數順序並不重要。因此:

    flip(add) = add 
    flip(mul) = mul 
    
  2. 對於我們並不需要創建額外的功能關係運算符:

    flip(lt) = gt 
    flip(gt) = lt 
    flip(lte) = gte 
    flip(gte) = lte 
    
  3. 我們只需要爲subdiv創建翻轉操作功能:

    var subFrom = x => y => x - y; // subFrom(5) means (y => 5 - y) 
    var divBy = y => x => x/y; // divBy(10) means (x => x/10) 
    

所有考慮的事情,我會說你應該用你的常識。

+0

我確實把'div(10)'看作「除以10」。我也讀'mult(3)'爲「乘以3」。我看到你最終也會升級到ES6語法。 – naomik

+0

嘿@naomik。是的,很多人把div(10)看成「除以10」而不是「10除」,這就是爲什麼我補充說「這種邊緣案例的正確論證順序是辯論的問題」。我爲'x => y => x/y'選擇名稱'div'的主要原因是缺乏更好的名稱。我正在考慮將它命名爲'divOver',以便我的命名約定與'subFrom'一致,但這似乎比直觀更令人困惑。是的,ES6語法比傳統的JS語法好得多。但是,我不會使用它,除非它被瀏覽器更廣泛地支持。 –

+0

我想你真的意思是'not(lte)=== gt'因爲'flip(lte)=== gte'。 – rand

相關問題