2014-09-21 79 views
-1

我正在解決舊的考試以練習SML。我發現一件令人感興趣的任務是:編寫一個函數repeat,該函數用簽名'a - >'a執行另一個函數。執行n次函數的咖喱函數

我假定所請求的功能是咖喱的功能和使用的o - 運算符:

fun repeat (1, f: 'a->'a) = f 
| repeat (n, f: 'a->'a) = f o repeat (n-1, f); 

然而,o運營商並沒有正式出來課程介紹,我不知道我怎麼能不寫這個?

+0

(注意倒票不是我)。你不需要使用'o'運算符(儘管這可能是有意義的......就像濫用這個運算符一樣),反正不是那樣。你必須使用'f'的前一個結果遞歸。注意問題的文本顯示爲「執行另一個函數」,而不是「返回函數」,並且您試圖在此返回一個函數。這是作業嗎? – Hibou57 2014-09-21 12:44:02

+0

@ Hibou57這個問題來自我正在努力練習的舊考試,因爲我將在下週三參加考試。 ---我認爲咖喱功能比普通版本更具挑戰性,所以我嘗試了,但沒有'o'就解決不了。你能否詳細說明你將如何解決它? – mafu 2014-09-21 14:24:41

+1

至於非咖喱版本,我想它會像'fun repeat(x,f:'a - >'a,0)= x | repeat(x,f,n)= repeat(f(x),f,n-1)'。 – mafu 2014-09-21 14:29:15

回答

3

不是那麼冗長,而是在某種程度上,最明確的,然後是後面,不詳細的解釋。

curried函數是獲取單個參數的函數。如果一個表達式有更多的參數,那麼嵌套的函數就是這麼多。第一個外層函數獲取一個參數,並且由內層函數構成,它可以由內層函數構成,等等。如此後所述(這是一種「部分評估」),任何這種內部函數都可能被返回,而不僅僅是最內層的函數。內部函數與外部函數的參數(形式上,參數綁定在閉包中)是「專用的」。

我們知道至少有一個函數參數f和整數參數counter。還需要有一個參數seed,第一次調用函數f

嵌套順序可以是任意的或指定的。如果沒有指定,我個人更傾向於在外部範圍內放置最少變化的參數,而在內部範圍內放置最多的參數。在這裏,我會說從最少到最不一樣:fcounterseed

這已經足以說明一個模板的開頭:

val repeat: ('a -> 'a) -> int -> 'a -> 'a = 
    fn f: 'a -> 'a => 
     fn count: int => 
     fn seed: 'a => 
      … 

我們已經實施了簽名的('a -> 'a) -> int -> 'a一部分。仍然是最後的-> 'a,這意味着要返回'a,並且它將通過內部循環進行評估。

環路可能是這種形式的東西(在僞代碼):

val rec loop = fn i => 
    if condition-to-stop 
     then return-something-or-`()` 
    else loop (i + 1) or (i - 1) 

如果循環計算的東西,它需要充當累加器一個額外的參數,並且將返回累加器作爲其最終結果。

實施循環,並把它的咖喱函數模板內部上方,我們可以得到:

val repeat: ('a -> 'a) -> int -> 'a -> 'a = 
    fn f: 'a -> 'a => 
     fn count: int => 
     fn seed: 'a => 
      let 
       val rec loop = fn (counter, x) => 
        if counter <= 0 then x 
        else loop (counter - 1, f x) 
      in 
       loop (count, seed) 
      end 

你瞭解這裏的let … in … end結構?

注意,你做了counter後衛可以使用一種模式,但作爲SML的整數可能是負的(存在SML沒有嚴格的自然),這是比較安全的抓住這個情況下也是如此,因此的if … then … else而不是一個模式匹配。里程可能會有所不同,但這不是問題的重點。

與前述相同,用fun代替val rec

fun repeat (f: 'a -> 'a) (count: int) (seed: 'a): 'a = 
    let 
     fun loop (counter, x) = 
     if counter <= 0 then x 
     else loop (counter - 1, f x) 
    in 
     loop (count, seed) 
    end 

註記repeat的參數沒有被,(既不是*)隔開。這是使用fun編寫一個咖喱功能的方式(相反,loop不是咖喱)。將其與之前的val版本的相同功能進行比較。如果沒有指定類型並且只有名稱,則可以省略括號。

的測試功能將被用作f參數:

val appendAnX = fn s: string => s^"x" 

測試:

val() = print (repeat appendAnX 5 "Blah:") 

咖喱功能比函數得到一個元組(這是正式一個參數更抽象的,因此也產生了咖喱味的功能,但這是另一個故事,有點作弊),因爲外部功能可能會部分應用:

這是一個部分應用程序,留下的最後一個參數,seed,未結合的:

val repeatAppendAnXThreeTimes = repeat appendAnX 3 

然後該功能可被應用於specifiying僅此seed

val() = print (repeatAppendAnXThreeTimes "Blah:") 

同樣,既counterseed可左右自由:

val repeatAppendAnX = repeat appendAnX 
val() = print (repeatAppendAnX 4 "Blah:") 

定義repeatAppendAnXThreeTimes的另一種方法。將其與上面的其他定義進行比較:

val repeatAppendAnXThreeTimes = repeatAppendAnX 3 
val() = print (repeatAppendAnXThreeTimes "Blah:") 
+0

謝謝你這個非常透徹的答案。我需要仔細閱讀,直到我完全理解它。鑑於你的結構化方式,我並不感到驚訝,我找不到解決方案,這裏有一些神奇的事情發生! – mafu 2014-09-21 17:16:39

+0

@mafu,這不是魔術,那只是關閉。這是lambda微積分的一個重要屬性。把它看作符號綁定。理解一個函數可以返回一個函數也很重要,就像在lambda演算中一樣,函數是其他表達式之間的表達式。 – Hibou57 2014-09-21 17:46:45