【问题标题】:Is there a way to capture a tuple of higher-order functions in Haskell?有没有办法在 Haskell 中捕获一组高阶函数?
【发布时间】:2017-11-05 08:43:11
【问题描述】:

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

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

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

【问题讨论】:

    标签: haskell pattern-matching higher-order-functions higher-order-types


    【解决方案1】:

    您永远不会在函数上进行模式匹配。您总是在配对构造函数 (,) 上进行模式匹配。您的(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)'type 对与 quotRem 的不同。尽管如此,还是可以编写你的函数:

    pairify :: (a -> (b, c)) -> (a -> b, a -> c)
    pairify f = (fst . f, snd . f)
    
    (q,r) = pairify (quotRem 12345)
    

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

    (even, odds) = pairify (partition even)
    

    【讨论】:

      【解决方案2】:

      (12345 `quotRem`)的类型:

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

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

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

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

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

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

      【讨论】:

        猜你喜欢
        • 2020-08-22
        • 1970-01-01
        • 2017-04-21
        • 1970-01-01
        • 2013-03-01
        • 2012-11-30
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多