簡體   English   中英

從函數定義Haskell推斷函數類型

[英]Inferring function type from function definition Haskell

因此,我正在接受有關Haskell的測試,一個問題說:

讓函數成為

lolo g x = ys
      where ys = [x] ++ filter (curry g x) ys

然后確定稱為lolo的函數的類型。 選項包括:

a) (a,b) -> Bool -> b -> [(a,b)]
b) (b -> b -> b) -> Bool -> [b]
c) ((b,b) -> Bool) -> b -> [b]
d) (a -> b -> Bool) -> b -> [c]

有人可以解釋是哪一個,為什么嗎? 我對此很困惑...我不明白的是:

1)咖喱功能只能適用於功能嗎? 不是可能是元組的數據類型? 那么您可以推斷g在這種情況下是一個函數? 如果g和x都是函數怎么辦? 是否可以將咖喱與第n個參數一起使用? 我只看到咖喱與1個參數一起使用。

2)我不太了解的另一件事是ys定義的遞歸。 因此ys由ys定義,但在這種情況下我看不到基本情況。 它會永遠結束嗎? 也許是遞歸結束的過濾功能。

3)還在咖喱中gx =咖喱(gx)對嗎? (這是有關函數應用優先級的問題)

非常感謝

1) curry的第一個參數必須是一個函數,這就是所謂的高階函數 ,它接受一個函數並返回一個新的函數。 雖然它的類型在GHCi中打印為

curry :: ((a, b) -> c) -> a -> b -> c

它更清楚地表示(IMO)為

curry :: ((a, b) -> c) -> (a -> b -> c)

顯然,它需要一個函數並返回一個新函數。 從技術上講,你可以說, curry接受3個參數,類型之一(a, b) -> c ,類型的一個a和類型之一b 它只需要一個通常接受參數元組的函數並將其轉換為帶有2個參數的函數。

2) ys的計算將永遠不會結束,不必費心嘗試在其上調用length ,您將永遠運行該計算。 不過,這不是問題,您可以使用無限列表和非終止列表就可以了(非終止是永遠需要計算下一個元素的列表,而不僅僅是具有無限元素的列表)。 不過,您仍然可以使用諸如“ take drop功能。

3) curry gx == curry (gx)嗎? 沒有! 當您看到類似abcde的表達式時, bcde都是a參數。 如果改為看到abc (de) ,則將e應用於d ,並將結果應用於abc 考慮filter even [1..10] ,這肯定與filter (even [1..10]) ,因為它甚至不會編譯! even :: Integral a => a -> Bool )。


解決此類問題時,首先查看您已經知道以下類型的表達式中使用了哪些函數:

(++)   :: [a] -> [a] -> [a]
filter :: (b -> Bool) -> [b] -> [b]
curry  :: ((c, d) -> e) -> c -> d -> e

我在每個變量中使用了不同的類型變量,以便在嘗試排列類型時會減少混亂。 您可以通過加載GHCi然后輸入以下內容來獲取這些類型

> :type (++)
(++) :: [a] -> [a] -> [a]
> -- Or just use :t
> :t filter
filter :: (a -> Bool) -> [a] -> [a]
> :t curry
curry :: ((a, b) -> c) -> a -> b -> c

如您所見,我將filter更改為使用b而不是a ,將curry更改為使用cde 除了fx = x + 1相對於fy = y + 1 ,這並沒有改變含義,只會使討論變得更容易。

現在我們已經將功能分解為子組件,我們可以從上至下進行工作。 最重要的是,我指的是最后一個被調用的函數,即(++) 您可以通過像這樣的樹來描繪此功能

    (++)
   /    \
[x]     filter
       /      \
    curry     ys
   /     \
  g       x

因此,我們可以清楚地看到(++)在頂部。 利用這一點,我們可以推斷, [x]具有式[a]這意味着x ~ a (波浪號的類型是平等符號)並且因此ys ~ [a]中,由於ys = [x] ++ something 現在我們知道了x的類型,我們可以開始填寫其余的表達式了。 接下來,我們努力filter (curry gx) ys 由於它是(++)的第二個參數,因此我們可以推斷出該子表達式也具有[a]類型。 如果我們看一下filter的類型:

filter :: (b -> Bool) -> [b] -> [b]

最終結果是[b]類型的列表。 由於已將其應用於[x] ++ ,因此我們可以推斷出該filter (curry gx) ys :: [a] 這意味着[b] ~ [a] => b ~ a 供參考,這使filter的類型

filter :: (a -> Bool) -> [a] -> [a]

現在,這對curry gx了約束,它必須適合filter的第一個參數,其類型a -> Bool 再次查看curry的類型:

curry :: ((c, d) -> e) -> c -> d -> e

這意味着, e ~ Bool ,和d ~ a 如果我們重新插入

curry :: ((c, a) -> Bool) -> c -> a -> Bool

現在忽略g ,我們看一下x的類型,我們發現它是a 因為x是第二個參數curry ,這意味着, x型的參數匹配c ,這意味着c ~ a 將其代入我們剛剛計算出的結果

curry :: ((a, a) -> Bool) -> a -> a -> Bool

               curry g x     :: a -> Bool
       filter (curry g x)    :: [a] -> [a]
       filter (curry g x) ys :: [a]
[x] ++ filter (curry g x) ys :: [a]

由此我們可以直接推斷出lolo的類型簽名以[a]結尾,因此

lolo :: ??? -> [a]

我將讓您執行其余的步驟來弄清楚是什么??? 是。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM