2013-11-29 27 views
0

我需要在CoffeeScript中進行一些高級的數組排序,並且我遇到了完全符合我的需求的thenBy.js微庫。它是用JavaScript編寫的,所以我將它翻譯成CoffeeScript,以便我可以在我的.coffee文件中內聯使用它,並且我在翻譯時遇到了一些問題。這不起作用:將JavaScript中的thenBy.js翻譯爲CoffeeScript的問題

firstBy = -> 

    # mixin for the `thenBy` property 
    extend = (f) -> 
    f.thenBy = tb 
    return f 

    # adds a secondary compare function to the target function (`this` context) 
    #which is applied in case the first one returns 0 (equal) 
    #returns a new compare function, which has a `thenBy` method as well 
    tb = (y) -> 
    x = this 
    return extend((a, b) -> 
     return x(a, b) or y(a, b) 
    ) 
    return extend 

但是,如果我用括號包裹,穿上後的括號,它的工作:

### Notice the starting paren 
firstBy = (-> 

    # mixin for the `thenBy` property 
    extend = (f) -> 
    f.thenBy = tb 
    return f 

    # adds a secondary compare function to the target function (`this` context) 
    #which is applied in case the first one returns 0 (equal) 
    #returns a new compare function, which has a `thenBy` method as well 
    tb = (y) -> 
    x = this 
    return extend((a, b) -> 
     return x(a, b) or y(a, b) 
    ) 
    return extend 
)() ### <- Notice the ending parens 

我有一個沒有運氣理解爲什麼把這些尾隨括號在事情導致它的工作。我知道我有一個匿名函數,然後用那些元素調用它(see this answer),但爲什麼這會起作用?

+0

你可以編譯到JS與兩個版本,並看到不同之處? – rdodev

+0

是的。它們都編譯得很好,而且在輸出JS和輸入之間,只有兩個不同。這真的只是我的一個問題,並不瞭解這個聰明的小片段是如何實際完成其工作的。爲什麼在定義它的時候需要調用它才能使其正常工作? – mattmc3

+0

只有在第一個例子中,你可以嘗試使用'=>'而不是' - >'來代替'extend'和'tb',並且讓我知道它是否適合你? – rdodev

回答

1

最後一個parens正在調用函數更大的函數(在第一個例子中定義爲firstBy),有效地將變量設置爲函數的返回值:extend函數。換句話說:

# Let firstBy1 be equivalent to your first definition, and 
# firstBy2 equivalent to your second. Then 
# firstBy1() is functionally equivalent to firstBy2 

console.log firstBy2 
### logs: 
# function (f) { 
#  f.thenBy = tb 
#  return f 
### } 

# So: 
func = (x, y) -> 
    # ... some comparison of x and y... 

foo = firstBy2(func) # or `foo = firstBy1()(func)` 
console.log foo.thenBy 
### logs: 
# function (y) { 
#  x = this 
#  return extend(function (a, b) { 
#  return x(a, b) or y(a, b) 
#  }) 
### } 

這可能是最好的一個例子來說明:

# For brevity/clarity... 
firstBy = firstBy2 

randomNums = 
    { attr1: 1, attr2: 2 }, 
    { attr1: 2, attr2: 8 }, 
    { attr1: 4, attr2: 2 }, 
    { attr1: 5, attr2: 2 }, 
    { attr1: 5, attr2: 3 }, 
    { attr1: 6, attr2: 1 }, 
    { attr1: 3, attr2: 1 }, 
    { attr1: 2, attr2: 4 } 

func1 = (x, y) -> 
    if x.attr1 == y.attr1 
    return 0 
    if x.attr1 > y.attr1 then 1 else -1 

func2 = (x, y) -> 
    if x.attr2 == y.attr2 
    return 0 
    if x.attr2 > y.attr2 then 1 else -1 

當我們調用...

randomNums.sort(firstBy(func1).thenBy(func2)) 

... firstBy(FUNC1)先求。它返回func1的與thenBy屬性:

func1 = (x, y) -> 
    if x.attr1 == y.attr1 
    return 0 
    if x.attr1 > y.attr1 then 1 else -1 

func1.thenBy = (y) -> 
    x = this 
    return extend((a, b) -> 
    return x(a, b) or y(a, b) 
) 

那麼,主叫firstBy(func1的).thenBy(FUNC2)調用附接到FUNC1新添加thenBy 參數,得到:

func1.thenBy(func2) = 
    extend((a, b) -> 
    func1(a, b) or func2(a, b) 
) 

的延伸函數然後將另一個(未調用的)thenBy屬性應用於此匿名函數,從而生成排序用於排序randomNums的最終函數。它基本上是在數組中的每對數字上調用func1,並且如果func1返回0(當每個對象的attr1相等時),則func2將在同一對上進行評估。隨着對thenBy的更多調用,這可以無限推斷。最後,我們的函數調用返回:

[ 
    { attr1: 1, attr2: 2 }, 
    { attr1: 2, attr2: 4 }, 
    { attr1: 2, attr2: 8 }, 
    { attr1: 3, attr2: 1 }, 
    { attr1: 4, attr2: 2 }, 
    { attr1: 5, attr2: 2 }, 
    { attr1: 5, attr2: 3 }, 
    { attr1: 6, attr2: 1 } 
] 

希望有幫助!

+0

啊,所以這是什麼啓用流利的API機制,即'firstBy(???)。thenBy(???)。thenBy(???)。etc'? – mattmc3

+1

是的,因爲firstBy和thenBy都返回內部擴展函數的實例。這段代碼是令人沮喪的遞歸,爲了清晰起見,我將以這個帖子爲例。 – mtoor

+0

同樣值得注意的是,您可以使用'do'關鍵字獲得相同的功能!不要在最後使用'('在開始和')()',而只需將其改爲'firstBy = do - >'...,然後調用該函數。您也不需要明確告訴CoffeeScript「返回」任何東西。 – phenomnomnominal