我理解Haskell中的it's impossible to pattern match functions,我完全理解为什么。但是,我有两个密切相关的问题。首先,如果您想部分应用函数以供以后使用,是否有一种方法可以定义和捕获返回值(如果它是一个元组)?或者我错了,这仍然是试图模仿我的鼻子下的匹配功能?
例如,假设我试图获得具有10的不同倍数的值的商和余数。那我怎么写这样的东西呢?
q, r :: Integral a => a -> a
(q, r) = (12345 `quotRem`)
我在这里意识到,存在单独的功能,所以我可以这样做:
q, r :: Integral a => a -> a
q = (12345 `quot`)
r = (12345 `rem`)
然而,这是一个非常具体的案例,并且有无限的其他函数示例返回元组,这些函数可以很好地概括。例如,一个返回列表中的平均数和赔率的函数。
evens, odds :: Integral a => [a] -> Int
(evens, odds) = (length . (filter even), length . (filter odd))
这引出了我的第二个问题。以上在GHCi中工作得很好。
Prelude> let (evens, odds) = (length . (filter even), length . (filter odd))
Prelude> :t evens
evens :: Integral a => [a] -> Int
Prelude> evens [1..10]
5
更令人困惑的是它甚至可以通过“模式匹配”工作,就像我在开始时使用(q, r)
一样:
Prelude> let evensOdds = (length . (filter even), length . (filter odd))
Prelude> :t evensOdds
evensOdds :: (Integral a1, Integral a) => ([a1] -> Int, [a] -> Int)
Prelude> let (ev,od) = evensOdds
Prelude> :t ev
ev :: Integral a1 => [a1] -> Int
Prelude> ev [1..10]
5
在加载到GHCi中的实际文件中它也可以正常工作,即使(evens, odds)
没有。为什么这两个不同,为什么第二个在GHCi中工作,如果它不能正常工作?可以通过某种方式利用这里的不同之处吗?
答案 0 :(得分:5)
你从不在一个函数上模式匹配。您始终在对构造函数(,)
上进行模式匹配。您的(even, odds)
示例
(evens, odds) = (length . (filter even), length . (filter odd))
就像
一样(first, second) = (x, y)
此时x
和y
的类型并不重要。
由于(q, r)
的类型,您的quotRem
示例无法正常工作。让我们回想一下,并将其与(q, r)
的类型进行比较:
quotRem :: Integral n => n -> n -> (n , n)
quotRem 12345 :: Integral n => n -> (n , n)
(q, r) :: Integral n => (n -> n, n -> n)
如您所见,对(q, r)
'类型与quotRem
'类型不同。仍然可以编写你的函数:
pairify :: (a -> (b, c)) -> (a -> b, a -> c)
pairify f = (fst . f, snd . f)
(q,r) = pairify (quotRem 12345)
但是你可以看到我们从pairify
获得的收益太多了。顺便说一句,来自partition
的{{1}}提供了Data.List
功能:
(even, odds)
答案 1 :(得分:3)
查看(12345 `quotRem`)
的类型:
Integral a => a -> (a, a)
这是一个返回元组的函数。如果您想将其变为功能元组,可以使用fst
和snd
撰写:
(q, r) = (fst . f, snd . f)
where f = (12345 `quotRem`)
如果您想以无点的方式执行此操作,一种方法是使用&&&
中的Control.Arrow
组合子。它的完全一般类型是:
Arrow a => a b c -> a b d -> a b (c, d)
专门针对->
箭头,即:
(b -> c) -> (b -> d) -> b -> (c, d)
因此它需要两个函数,每个函数都采用b
类型的值,并在元组中返回它们的结果(类型c
和d
)。所以在这里你可以这样做:
split = (fst .) &&& (snd .)
(q, r) = split (12345 `quotRem`)
然而,如果你看一下(length . filter even, length . filter odd)
的类型,它已经是一个元组,
(Integral a, Integral b) => ([a] -> Int, [b] -> Int)
这就是为什么你可以解构这个元组以绑定evens
和odds
。