2

我知道在 Haskell中不可能对函数进行模式匹配,我完全理解为什么。但是,我有两个密切相关的问题。首先,如果您想部分应用函数以供以后使用,如果它是一个元组,是否有定义和捕获返回的方法?还是我错了,这仍在尝试在我的眼皮底下匹配功能?

例如,假设我试图获得一个十的多个倍数的商和余数。那么,我该如何写这样的东西呢?

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 中工作?可以以某种方式利用这里的不同之处吗?

4

2 回答 2

5

您永远不会在函数上进行模式匹配。您总是在 pair-constructor 上进行模式匹配(,)。你的(even, odds)例子

(evens, odds) = (length . (filter even), length . (filter odd))

就像

(first, second) = (x, y)

在那一点上,什么类型xy有什么都没有关系。


由于' 类型,您的(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)

如您所见,pair (q, r)'type 与 'type 不同quotRem。尽管如此,还是可以编写你的函数:

pairify :: (a -> (b, c)) -> (a -> b, a -> c)
pairify f = (fst . f, snd . f)

(q,r) = pairify (quotRem 12345)

但正如你所见,我们并没有从中获得太多收益pairify。顺便说一句,partitionfromData.List提供您的(even, odds)功能:

(even, odds) = pairify (partition even)
于 2017-11-05T10:17:46.263 回答
3

查看类型(12345 `quotRem`)

Integral a => a -> (a, a)

它是一个返回元组的单个函数。如果你想把它变成一个函数的元组,你可以用fstand组合它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,并在一个元组中返回它们的结果(类型cd)。所以在这里你可以做这样的事情:

split = (fst .) &&& (snd .)
(q, r) = split (12345 `quotRem`)

而如果您查看 的类型(length . filter even, length . filter odd),它已经是一个元组,

(Integral a, Integral b) => ([a] -> Int, [b] -> Int)

这就是为什么你当然可以解构这个元组来绑定evensodds

于 2017-11-05T10:23:44.337 回答