我想要实现像
这样的东西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]]
为此,我想以递归方式应用&lt; *&gt;喜欢
foldx (h:w) = h <*> foldx w
foldx (w:[]) = w
但似乎无法制作&lt; *&gt;递归的。
答案 0 :(得分:4)
让我们玩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
使用(<*>)
似乎是一种自然的方法。
答案 1 :(得分:4)
如果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)]
答案 2 :(得分:2)
一般来说,折叠(<*>)
因其类型而变得棘手,正如其他人所提到的那样。但是对于您的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
答案 3 :(得分:0)
为此,我想递归地应用
<*>
之类的foldx (h:w) = h <*> foldx w foldx (w:[]) = w
但似乎无法制作&lt; *&gt;递归的。
我认为你对左右相关性感到困惑。 danidiaz用foldr (<*>)
重新表述了这一点,这对于这种分析非常有用。文档提供了a useful definition of foldr
in terms of expansion:
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)
形式的类型通常称为 endomorphisms ,它们形成一个monoid,其中作为操作,id
作为标识。 newtype
中有一个Data.Monoid
封装器:
newtype Endo a = Endo { appEndo :: a -> a }
instance Monoid (Endo a) where
mempty = id
mappend = (.)
这为我们提供了另一种思考foldr (<*>)
的方式,通过Endo
来表达{/ 1}}:
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
答案 4 :(得分:0)
你拥有的东西相当于zipWith3 (\x y z -> x*y+z) [4,7] [6,9] [5,10]
。
foldl
<*>
foldl
<*>
foldl :: (a -> b -> a) -> a -> [b] -> a
{em}> > 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
,但是当您在第一个数字列表中应用三元函数时,您会得到一个列表二进制函数,然后是下一步的一元函数,最后只有数字(所有不同的类型,然后):
zipWithN
chi的答案解决了这个问题,但 arity是固定的(针对特定代码)。实际上,答案真正做的是定义ZipList
的限制版本(嗯,这里,当与N
应用程序一起使用时 - 显然,它适用于任何一般的应用程序) {1}}(但仅适用于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])
works)。