繁体   English   中英

是否可以在haskell中折叠<*>?

[英]Is it possible to fold <*> in haskell?

我想要实现类似的东西

fun1 f a_ziplist

例如

getZipList $ (\x y z -> x*y+z) <$> ZipList [4,7] <*> ZipList [6,9] <*> ZipList [5,10]

f = (\x y z -> x*y+z) 
ziplist = [[4,7],[6,9],[5,10]]

为此,我想递归地应用<*>之类的

foldx (h:w) = h <*> foldx w
foldx (w:[]) = w

但似乎不可能使<*>递归。

让我们玩ghci中的类型,看看他们带我们的位置。

λ import Control.Applicative

(<*>)的类型

λ :t (<*>)
(<*>) :: Applicative f => f (a -> b) -> f a -> f b

foldr的类型:

λ :t Prelude.foldr
Prelude.foldr :: (a -> b -> b) -> b -> [a] -> b

也许我们可以使用(<*>)作为作为foldr的第一个参数传递的函数。 会是什么类型的?

λ :t Prelude.foldr (<*>)
Prelude.foldr (<*>) :: Applicative f => f a -> [f (a -> a)] -> f a

因此,似乎它在应用上下文中采用初始值,并在应用上下文中使用函数列表,并返回另一个应用程序。

例如,使用ZipList作为应用程序:

λ getZipList $ Prelude.foldr (<*>) (ZipList [2,3]) [ ZipList [succ,pred], ZipList [(*2)] ]

结果是:

[5]

我不确定这是否是问题的意图,但它似乎是一种自然的fold方法(<*>)

如果ziplist参数必须是普通列表,则看起来不可能。 这是因为fold f [a1,...,an]必须为每个n键入良好类型,因此f必须是一个函数类型,每个n至少需要n参数,因此无限多。

但是,如果您使用GADT列表类型,其中值将其长度显示为类型级自然,您可以实现类似于您想要的类型。

{-# LANGUAGE DataKinds, KindSignatures, TypeFamilies, GADTs #-}

import Control.Applicative

-- | Type-level naturals
data Nat = Z | S Nat

-- | Type family for n-ary functions
type family   Fn (n :: Nat) a b
type instance Fn Z     a b = b
type instance Fn (S n) a b = a -> Fn n a b

-- | Lists exposing their length in their type
data List a (n :: Nat) where
  Nil :: List a Z
  Cons :: a -> List a n -> List a (S n)

-- | General <*> applied to a list of arguments of the right length
class Apply (n :: Nat) where
   foldF :: Applicative f => f (Fn n a b) -> List (f a) n -> f b

instance Apply Z where
   foldF f0 Nil = f0

instance Apply n => Apply (S n) where
   foldF fn (Cons x xs) = foldF (fn <*> x) xs

test :: [(Integer,Integer,Integer)]
test = foldF (pure (,,)) (Cons [10,11] (Cons [20,21] (Cons [30,31] Nil)))
-- Result: [(10,20,30),(10,20,31),(10,21,30),(10,21,31)
--         ,(11,20,30),(11,20,31),(11,21,30),(11,21,31)]

通常,折叠(<*>)因类型而变得棘手,正如其他人所提到的那样。 但是对于您的特定示例,您的ziplist元素的类型都相同,您可以使用不同的方法,并使您的计算只需对f进行一些小的更改,以使其采用list参数而不是单个元素:

import Data.Traversable
import Control.Applicative

f = (\[x,y,z] -> x*y+z) 
ziplist = [[4,7],[6,9],[5,10]]

fun1 f l = getZipList $ f <$> traverse ZipList l

甚至可以通过Data.ListPrelude函数来实现这一点:

fun1 f = map f . transpose

为此,我想递归地应用<*>类的

 foldx (h:w) = h <*> foldx w foldx (w:[]) = w 

但似乎不可能使<*>递归。

我认为你对左右相关性感到困惑。 danidiaz根据foldr (<*>)重新表述,这对此分析非常有用。 该文档根据扩展提供了有用的foldr定义

foldr f z [x1, x2, ..., xn] == x1 `f` (x2 `f` ... (xn `f` z) ...)

所以将其应用于您的案例:

foldr (<*>) z [x1, x2, ..., xn] == x1 <*> (x2 <*> ... (xn <*> z) ...)

请注意parens。 <*>是左关联的,因此foldr扩展不同于:

x1 <*> x2 <*> ... <*> xn <*> z == ((... (x1 <*> x2) <*> ...) <*> xn) <*> z

让我们更多地考虑一下foldr (<*>)含义。 另一种思考方式是稍微重写一下:

flip (foldr (<*>)) :: Applicative f :: [f (a -> a)] -> f a -> f a

形式(a -> a)类型通常被称为内同胚 ,它们形成一个幺半群,其中组成为操作, id为身份。 有一个newtype在包装Data.Monoid这些:

newtype Endo a = Endo { appEndo :: a -> a }

instance Monoid (Endo a) where
    mempty = id
    mappend = (.)

这给了我们另一种思考foldr (<*>) ,通过用Endo来表示它:

toEndo :: Applicative f => f (a -> a) -> Endo (f a)
toEndo ff = Endo (ff <*>)

然后,基本上, foldr (<*>)作用是减少这个幺半群:

foldrStar :: Applicative f => [f (a -> a)] -> Endo (f a)
foldrStar fs = mconcat $ map toMonoid fs

你拥有的相当于zipWith3 (\\xyz -> x*y+z) [4,7] [6,9] [5,10]

foldl <*>是不可能的(你需要foldl因为<*>关联到左边 )因为foldl :: (a -> b -> a) -> a -> [b] -> a即它是同aa -> b -> a ,但是当你申请号码的第一个列表上的三元函数,你得到下一步的二元函数,然后一元函数的列表,只有最后,数字(所有不同类型的, 然后):

>> let xs = map ZipList [[4,7],[6,9],[5,10]]    
>> getZipList $ pure (\x y z -> x*y+z) <*> (xs!!0) <*> (xs!!1) <*> (xs!!2)
[29,73]
>> foldl (<*>) (pure (\x y z -> x*y+z)) xs

<interactive>:1:6:
    Occurs check: cannot construct the infinite type: b = a -> b
      Expected type: f (a -> b)
      Inferred type: f b
    In the first argument of `foldl', namely `(<*>)'
    In the expression: foldl (<*>) (pure (\ x y z -> x * y + z)) xs

>> :t foldl
foldl ::                   ( a         ->  b  ->  a ) -> a -> [b] -> a
>> :t (<*>)
(<*>) :: (Applicative f) => f (a -> b) -> f a -> f b    -- f (a -> b) = f b

chi的答案解决了这个问题,但是arity是固定的 (对于特定的代码)。 实际上,那个答案真正做的是定义(受限制的版本) zipWithN (好吧,这里,当与ZipList应用程序ZipList使用时 - 显然,它适用于任何一般的应用程序)对于任何N (但仅适用于a -> a -> a -> ... -> a函数),而例如

zipWith7 :: (a -> b -> c -> d -> e -> f -> g -> h) -> 
            [a] -> [b] -> [c] -> [d] -> [e] -> [f] -> [g] -> [h]

(换句话说, zipWith3 (,,) [10,11] ([20,21]::[Integer]) ([30,31]::[Int]) 起作用 )。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM