代码之家  ›  专栏  ›  技术社区  ›  Travis Brown

使用列表中的项作为参数

  •  10
  • Travis Brown  · 技术社区  · 15 年前

    假设我有一个具有以下类型签名的函数:

    g :: a -> a -> a -> b
    

    我还有一张清单 a 让我们称之为 xs 我知道至少包含三个项目。我想申请 g 前三项 XS . 我知道我可以定义如下的组合器:

    ($$$) :: (a -> a -> a -> b) -> [a] -> b
    f $$$ (x:y:z:_) = f x y z
    

    那我就可以用 g $$$ xs . 这使得 $$$ 有点像 uncurry ,但对于具有三个相同类型参数和列表(而不是元组)的函数。

    有没有一种方法可以用标准的组合器以惯用方式来实现这一点?或者更确切地说,在Haskell中,最惯用的方法是什么?我想试试看 pointfree 在非中缀版本上 $$ 也许能让我知道从哪里开始,但产量是令人憎恶的10 flip S,少数 head S和 tail S和 ap 和28个圆括号。

    (注:我知道一开始这不是一件非常简单的事情,但我遇到过一些情况,特别是在使用parsec时,这似乎是一个合理的解决方案。我当然会接受“不要 曾经 如果这是最好的答案,那么用真正的代码来做这个,但是我更愿意看到一些巧妙的技巧,包括 ((->) r) Monad或者其他什么的。)

    2 回复  |  直到 15 年前
        1
  •  12
  •   C. A. McCann Ravikant Cherukuri    15 年前

    或者更确切地说,在Haskell中,最惯用的方法是什么?

    Idiomatic?如果你真的想要一个函数 ($$$) 是的,您所拥有的代码可能和您将获得的代码一样惯用。

    我更喜欢看一些巧妙的把戏

    哦,嗯,在 那个 案例。

    {-# LANGUAGE MultiParamTypeClasses #-}
    {-# LANGUAGE FunctionalDependencies #-}
    {-# LANGUAGE FlexibleInstances #-}
    {-# LANGUAGE OverlappingInstances #-}
    {-# LANGUAGE UndecidableInstances #-}
    class ListApply f a r | f -> a r where
        ($...) :: f -> [a] -> r
    
    instance (TypeCast b r) => ListApply b a r where
        x $... _ = typeCast x
    
    instance (ListApply f a r) => ListApply (a -> f) a r where
        f $... (x:xs) = (f x) $... xs
    

    好了,一个完全通用的解决方案:给定一个带有签名的任意arity函数 a -> a ... -> b ,将其应用于列表中的任意多个元素 [a] 必要时。演示:

    ones :: [Int]
    ones = repeat 1
    
    test1 x = x
    test2 x y = x + y
    test3 x y z = (x + z) * (y + z)
    

    在GHCi:

    > test1 $... ones
    1
    > test2 $... ones
    2
    > test3 $... ones
    4
    

    如果这是最好的答案,我肯定会接受“永远不要用真正的代码来做这个”。

    你可能想和它一起去。


    哦,还有一点运行上述代码所需的样板文件:

    class TypeCast   a b   | a -> b, b->a   where typeCast   :: a -> b
    class TypeCast'  t a b | t a -> b, t b -> a where typeCast'  :: t->a->b
    class TypeCast'' t a b | t a -> b, t b -> a where typeCast'' :: t->a->b
    instance TypeCast'  () a b => TypeCast a b where typeCast x = typeCast' () x
    instance TypeCast'' t a b => TypeCast' t a b where typeCast' = typeCast''
    instance TypeCast'' () a a where typeCast'' _ x  = x
    

    这是瑞士军刀的类型级元编程,由 Oleg Kiselyov .

        2
  •  7
  •   sdcvvc    15 年前
    f $$$ (x:y:z:_) = f x y z
    

    在我看来,这是最惯用和简洁的方式。如果参数的数目不同,可以使用模板haskell,也可以重复执行-定义:

    zero = const
    next n f (x:xs) = n (f x) xs
    

    那么你的功能是 next (next (next zero))) 这对任何 next .

    也可以将其分解为更多的基本组合器:

    firstThree (x:y:z:_) = (x,y,z)
    uncurry3 f (x,y,z) = f x y z
    g f = uncurry3 f . firstThree
    
    推荐文章