有没有办法在 Haskell 中捕获高阶函数的元组?

Is there a way to capture a tuple of higher-order functions in Haskell?

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

例如,假设我正在尝试获取一个值的各种十的倍数的商和余数。那我怎么写这样的东西呢?

q, r :: Integral a => a -> a
(q, r) = (12345 `quotRem`)

我意识到这里存在单独的函数,所以我可以这样做:

q, r :: Integral a => a -> a
q = (12345 `quot`)
r = (12345 `rem`)

但是,这是一个非常特殊的情况,return 元组的函数还有无数其他示例可以很好地概括。例如,returns 列表中偶数和奇数的函数。

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

更令人困惑的是,"pattern-matching" 的工作方式与我一开始玩 (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 中完全可以工作?可以以某种方式利用这里的不同之处吗?

您永远不会在函数上进行模式匹配。您总是在对构造函数 (,) 上进行模式匹配。您的 (even, odds) 示例

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

就像

一样工作
(first, second) = (x, y)

此时 xy 的类型无关紧要。


由于 quotRem 的类型,您的 (q, r) 示例不起作用。让我们回忆一下,并与(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 中获得太多收益。顺便说一句,Data.List 中的 partition 提供了您的 (even, odds) 功能:

(even, odds) = pairify (partition even)

(12345 `quotRem`)的类型:

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

returns一个元组是一个函数。如果你想把它做成一个函数元组,你可以用 fstsnd:

组合它
(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 类型的值,并且 returns 它们的结果(cd 类型)在一个元组中.所以在这里你可以做这样的事情:

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

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

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

这就是为什么您当然可以解构此元组以绑定 evensodds.