考慮型別
foo :: a -> ((b -> a) -> c) -> c
我想創建一個滿足這種型別的函式......
我知道foo x y = y (\z -> x)滿足:typeGHCi 中確認的這種型別,但是我將如何手動或逐步獲得這個最終功能?
我也知道foo2 f g = \x -> f (g x)也會滿足foo2 :: (a -> b) -> (c -> a) -> c -> b,但也不知道如何使用此功能。
uj5u.com熱心網友回復:
這只是將拼圖的匹配部分組合在一起的問題。同名棋子匹配。的型別foo意味著它是一個有兩個引數的函式:
foo :: a -> ((b -> a) -> c) -> c
-- a f
foo a f = c
-- a :: a
-- f g :: c
-- g b :: a
where
c = f g -- produce a c-type value
g b = a -- define g to produce
-- the value a
f我們得到了,但g我們必須發明它才能回傳a;幸運的是,我們已經得到了一個a:
-- f :: ((b -> a) -> c)
-- g
-- g :: b -> a
a作為 的引數提供給我們foo,并且b只是 的未使用引數g。
接下來我們可以替換和簡化,使用以下事實:
p :: q -> r
p q = r...
和寫這個是一樣的:
p :: q -> r
p = \q -> r...
我們r...用來表示型別為 的運算式r。
也s -> t -> r一樣,s -> (t -> r)因為,同樣,
h :: s -> t -> r
h s t = r...
h s = (\t -> r...)
h = \s -> (\t -> r...)
都表達相同的東西。
你的第二個問題可以用同樣的方式解決。
x :: a 表示“值 x 具有型別 a”; a :: a 表示“值 a 具有型別 a”。同一個名字a 可以用于兩個不同的角色;當你習慣它時,它實際上是一個非常有用的助記符。
另請參閱。
uj5u.com熱心網友回復:
下劃線_或以下劃線開頭的未定義名稱(_what例如找出如何迭代地填寫程式。謝天謝地,這是一個非常有用的案例,所以我可以在 GHCi 中輕松一步一步地完成它(并>指示提示)。我們從一個洞開始foo = _:
> foo :: a -> ((b -> a) -> c) -> c; foo = _
我們收到一條訊息說Found hole: _ :: a -> ((b -> a) -> c) -> c。GHC 建議foo :: a -> ((b -> a) -> c) -> c作為一種有效的填補漏洞的方法,并且確實foo = foo是合法的,但這不是我們正在尋找的答案,因為它代表了一個無限回圈。
相反,我們可以查看型別并從上到下將其分解為多個部分。我們從一個函式型別 ( ->) 開始,它的輸入是a,輸出是((b -> a) -> c) -> c。我們可以使用 lambda 運算式創建一個函式值,為該a值發明一個名稱,比如x,然后為函式體放置另一個孔:
> foo :: a -> ((b -> a) -> c) -> c; foo = \ x -> _
…
? Found hole: _ :: ((b -> a) -> c) -> c
…
它現在提到我們可以x :: a使用,除了foo,但這還不夠,所以接下來我們可以((b -> a) -> c) -> c分解它的輸入((b -> a) -> c)和輸出c。(從現在開始,為簡潔起見,我也將跳過重復的型別簽名foo。)
> foo :: …; foo = \ x -> \ f -> _
…
? Found hole: _ :: c
…
我們不能c進一步分解型別變數,我們知道我們需要創建一個型別的值c,所以我們可以看看我們有什么可用的:
f :: (b -> a) -> cx :: afoo :: a -> ((b -> a) -> c) -> c
所以我們的選擇是:
遞回呼叫
foo稱呼
f
#1 會很快讓我們回到我們所處的情況,所以 #2 就是剩下的了。f期望一個型別的值b -> a作為引數:
> foo :: …; foo = \ x -> \ f -> f _
…
? Found hole: _ :: b -> a
…
所以我們可以再次撰寫一個 lambda:
> foo :: …; foo = \ x -> \ f -> f (\ y -> _)
…
? Found hole: _ :: a
…
我們可以產生未知型別值的唯一方法a是使用x :: a我們已經給出的值,所以最終結果是這樣的:
foo = \ x -> \ f -> f (\ y -> x)
這可以用與您提出問題的形式完全相同的形式撰寫,使用一點語法糖和幾個不同的變數名稱選擇:
foo = \ x -> \ f -> f (\ y -> x)
foo x = \ f -> f (\ y -> x)
foo x f = f (\ y -> x)
foo x y = y (\ z -> x)
uj5u.com熱心網友回復:
問題 1
foo :: a -> ((b -> a) -> c) -> c
說 foo 必須接受一個a和一個帶有型別的函式(b -> a) -> c并以某種方式產生一個 c。
foo a function = c
我們可以從哪里a::a得到function::(b -> a) -> c
c?
好訊息:function對c我們有利。
讓我們使用它。function::(b->a) -> c所以我們可以得到 ac如果我們給它 a (b->a)。
所以我們首先需要創建一個輔助函式,它接受bs 并給我們as:
foo a function =
let helper b = a
沒關系!我已經有了一個a,所以我可以使用它。我們只需要交出我們剛剛制作function的helper:
foo :: a -> ((b -> a) -> c) -> c
foo a function =
let helper b = a
in function helper
現在注意helper忽略它的輸入,所以我們可以寫
foo :: a -> ((b -> a) -> c) -> c
foo a function =
let helper _ = a
in function helper
我們可以使用 lambda 表示法作為助手:
foo :: a -> ((b -> a) -> c) -> c
foo a function =
let helper = \_ -> a
in function helper
但現在我們可以只寫(\_ -> a)而不是 helper:
foo :: a -> ((b -> a) -> c) -> c
foo a function =
function (\_ -> a)
最后,讓我們縮寫function為f:
foo :: a -> ((b -> a) -> c) -> c
foo a f = f (\_ -> a)
問題2
foo2 :: (a -> b) -> (c -> a) -> c -> b
好的,這次我們需要制作一個b. 我們得到了一個c和幾個函式來將as 變成bs 和cs 變成as:
foo2 :: (a -> b) -> (c -> a) -> c -> b
foo2
make_b_from_a
make_a_from_c
c
= b
好吧,唯一能給我們的b是make_b_from_a:
foo2 :: (a -> b) -> (c -> a) -> c -> b
foo2
make_b_from_a
make_a_from_c
c
= make_b_from_a a
但現在我們需要一個a,但我們可以使用make_a_from_c它,我們已經有一個c:
foo2 :: (a -> b) -> (c -> a) -> c -> b
foo2
make_b_from_a
make_a_from_c
c
= make_b_from_a (make_a_from_c c)
讓我們縮短名稱。我將呼叫(c->a)一個f,因為我們在另一個之前使用它,我將呼叫它g:
foo2 :: (a -> b) -> (c -> a) -> c -> b
foo2 g f c = g (f c)
我們可以使用 lambda 獲取最后一個引數:
foo2 :: (a -> b) -> (c -> a) -> c -> b
foo2 g f = \c -> g (f c)
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/462868.html
