[英]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.List
和Prelude
函数来实现这一点:
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
即它是同a
在a -> 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.