如何在 Haskell 中添加兩個元組的元素以得到第三個元組。簽名是這樣的,
Add :: (Int,Int) -> (Int,Int) ->(Int,Int)
Add a b = ....
到目前為止,我只能想到這一點:
Add a b = [(x, y) | a = (x1, y1), b = (x2, y2), x=x1 x2, y =y1 y2n]
然而,我對 Haskell 很陌生,所以我所做的甚至正確嗎?
uj5u.com熱心網友回復:
你在做什么是不正確的。串列推導式不是執行此操作的正確方法。
使用模式匹配來提取元組的元素:
add :: (Int, Int) -> (Int, Int) -> (Int, Int)
add (x, y) (u, v) = (x u, y v)
使用fst和提取元組的元素snd:
add2 :: (Int, Int) -> (Int, Int) -> (Int, Int)
add2 x y = (fst x fst y, snd x snd y)
還要記住,函式不能在 Haskell 中以大寫字母開頭。
uj5u.com熱心網友回復:
關于你的嘗試,嘗試運行它ghci,你會看到一個錯誤。至于為什么,您嘗試使用的語法稱為list comprehension,并在此處記錄;創建串列是語法上的 surgar ,例如[1,2,3],因此它不是適合您的工具,因為能夠對具有型別的對進行求和,而(·,·)不是[·](·我的意思是“任何型別”)。
下面是撰寫您想要的函式的非基本(可能是矯枉過正)的方法。但是設定理解的目標可能是一種迫使自己深入了解 Haskell 的方法。
這里是:
import Data.Bifunctor (bimap)
import Data.Tuple.Extra (both)
sumTwoTuples x y = uncurry bimap (both ( ) x) y
這是如何運作的?讓我們來看看
> :t both
both :: (a -> b) -> (a, a) -> (b, b)
Soboth接受一個函式并將其應用于一對的兩個元素;因此both ( )將適用( )于第一對的兩邊x;如果是(3,4),你會得到((3 ), (4 ))(是的,我會把它寫成(3 ,4 ),但這是非法的語法)。
現在我們有了這對函式both ( ) x,我們想將它們中的每一個應用到這對函式的一側y。
這是bimap:
> :t bimap
bimap :: Bifunctor p => (a -> b) -> (c -> d) -> p a c -> p b d
所以它需要 2 個函式并將其應用于 a 的每一側Bifunctor(在我們的例子中Bifunctor型別p是(,))。
這幾乎就是我們所需要的,但它將兩個函式作為兩個單獨的引數,而不是包含它們的一對。
uncurry 給出了一種調整方法:
> :t uncurry
uncurry :: (a -> b -> c) -> (a, b) -> c
確實,uncurry bimap有這種型別:
> :t uncurry bimap
uncurry bimap :: Bifunctor p => (a -> b, c -> d) -> p a c -> p b d
因此它需要一個雙功能和每個適用于對對應的側y,其具有型別p b d與p被(,)在我們的情況。
uj5u.com熱心網友回復:
函式最終生成的值( 的型別以 a開頭,但串列的型別以 a 開頭[。他們不能匹配,所以你的方法不可能是正確的。但我們可以修補它。
您嘗試將值與變數元組進行模式匹配是正確的,但實際上模式在等號的左側,值在等號的右側。它必須在 a 內let:
add1 :: (Int,Int) -> (Int,Int) -> [(Int,Int)] -- NB: added brackets!
add1 a b = [(x, y) | let (x1, y1) = a ; (x2, y2) = b ;
x = x1 x2 ; y = y1 y2 ]
實際上我們并不需要一個let 內部串列理解,
add2 :: (Int,Int) -> (Int,Int) -> [(Int,Int)]
add2 a b = let { (x1, y1) = a ; (x2, y2) = b ;
x = x1 x2 ; y = y1 y2 } in
[(x, y)]
現在我們可以去掉那些括號,得到你想要的值和型別。
還有一個,讓你的原始代碼作業而無需改變任何事情有點棘手的方式在它(除了固定的錯誤的資本add,并使其正確的let,當然語法)。
我們只需添加一個單詞并啟用一個擴展名,它就可以作業:
{-# LANGUAGE MonadComprehensions #-}
import Data.Function.Identity
add :: (Int,Int) -> (Int,Int) -> (Int,Int)
add a b = magicWord
[(x, y) | let { (x1, y1) = a ; (x2, y2) = b ;
x = x1 x2 ; y = y1 y2 } ]
神奇的詞是
magicWord = runIdentity
有了這個擴展,定義的推斷型別是
add :: (Num t1, Num t, Monad m)
=> (t, t1) -> (t, t1) -> m (t, t1)
并且因為
runIdentity :: Identity a -> a
使用它強制m ~ Identity并且它只是有效:派生型別是
(Num t1, Num t, Monad m)
=> (t, t1) -> (t, t1) -> m (t, t1)
Identity a -> a
--------------------------------------------
m ~ Identity , Monad Identity
(t,t1) ~ a
--------------------------------------------
(Num t1, Num t)
=> (t, t1) -> (t, t1) -> (t, t1)
匹配您給定的型別簽名
(Int, Int) -> (Int, Int) -> (Int, Int)
因為Int在Num,
并且Identity確實是一個單子,一個什么都不做,
newtype Identity a = Identity {runIdentity :: a}
fmap f (Identity a) = Identity (f a)
join (Identity (Identity a)) = Identity a
除了用它的標簽標記值,它甚至會立即消失,因為型別Identity a被定義為newtype,而不是data。
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/316867.html
