我正在解決舊的考試以練習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
運營商並沒有正式出來課程介紹,我不知道我怎麼能不寫這個?
我正在解決舊的考試以練習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
運營商並沒有正式出來課程介紹,我不知道我怎麼能不寫這個?
不是那麼冗長,而是在某種程度上,最明確的,然後是後面,不詳細的解釋。
curried函數是獲取單個參數的函數。如果一個表達式有更多的參數,那麼嵌套的函數就是這麼多。第一個外層函數獲取一個參數,並且由內層函數構成,它可以由內層函數構成,等等。如此後所述(這是一種「部分評估」),任何這種內部函數都可能被返回,而不僅僅是最內層的函數。內部函數與外部函數的參數(形式上,參數綁定在閉包中)是「專用的」。
我們知道至少有一個函數參數f
和整數參數counter
。還需要有一個參數seed
,第一次調用函數f
。
嵌套順序可以是任意的或指定的。如果沒有指定,我個人更傾向於在外部範圍內放置最少變化的參數,而在內部範圍內放置最多的參數。在這裏,我會說從最少到最不一樣:f
,counter
seed
。
這已經足以說明一個模板的開頭:
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:")
同樣,既counter
和seed
可左右自由:
val repeatAppendAnX = repeat appendAnX
val() = print (repeatAppendAnX 4 "Blah:")
定義repeatAppendAnXThreeTimes
的另一種方法。將其與上面的其他定義進行比較:
val repeatAppendAnXThreeTimes = repeatAppendAnX 3
val() = print (repeatAppendAnXThreeTimes "Blah:")
(注意倒票不是我)。你不需要使用'o'運算符(儘管這可能是有意義的......就像濫用這個運算符一樣),反正不是那樣。你必須使用'f'的前一個結果遞歸。注意問題的文本顯示爲「執行另一個函數」,而不是「返回函數」,並且您試圖在此返回一個函數。這是作業嗎? – Hibou57 2014-09-21 12:44:02
@ Hibou57這個問題來自我正在努力練習的舊考試,因爲我將在下週三參加考試。 ---我認爲咖喱功能比普通版本更具挑戰性,所以我嘗試了,但沒有'o'就解決不了。你能否詳細說明你將如何解決它? – mafu 2014-09-21 14:24:41
至於非咖喱版本,我想它會像'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