对于简单的功能组合,是否存在“符号”语法糖?
(即(.) :: (b -> c) -> (a -> b) -> a -> c
)
我希望能够存储一些作品的结果供以后使用(同时仍然继续使用链条。
如果可能,我宁愿不使用RebindableSyntax扩展名。
我正在寻找类似的东西:
composed :: [String] -> [String]
composed = do
fmap (++ "!!!")
maxLength <- maximum . fmap length
filter ((== maxLength) . length)
composed ["alice", "bob", "david"]
-- outputs: ["alice!!!", "david!!!"]
我不确定这样的事情是否可能,因为早期函数的结果基本上必须通过“通过”maxLength的绑定,但我愿意听到任何其他类似的表达选项。基本上我需要在整理作文时收集信息,以便以后使用。
也许我可以用状态monad做这样的事情?
感谢您的帮助!
修改
这种事情有点起作用:
split :: (a -> b) -> (b -> a -> c) -> a -> c
split ab bac a = bac (ab a) a
composed :: [String] -> [String]
composed = do
fmap (++ "!!!")
split
(maximum . fmap length)
(\maxLength -> (filter ((== maxLength) . length)))
答案 0 :(得分:5)
实现类似目标的一种可能方式是箭头。基本上,在“存储插页式结果”中,您只是将信息流分解为组合链。这就是&&&
(fanout)组合子的作用。
import Control.Arrow
composed = fmap (++ "!!!")
>>> ((. length) . (==) . maximum . fmap length &&& id)
>>> uncurry filter
但这绝对不是人类易于理解的代码。
状态monad似乎也允许相关的东西,但问题是状态类型是通过do
块的monadic链来修复的。这并不足以灵活地在整个构图链中获取不同类型的值。虽然有可能规避这一点(其中,确实是RebindableSyntax
),但这也不是IMO的好主意。
答案 1 :(得分:3)
专用于(<*>)
的函数实例的Applicative
类型为:
(<*>) :: (r -> a -> b) -> (r -> a) -> (r -> b)
生成的r -> b
函数将其参数传递给r -> a -> b
和r -> a
函数,然后使用a
函数生成的r -> a
值作为r -> a -> b
一个的第二个参数。
这与你的功能有什么关系? filter
是两个参数的函数,谓词和列表。现在,您要做的一个关键方面是谓词是从列表生成的。这意味着您的函数核心可以用(<*>)
:
-- Using the predicate-generating function from leftaroundabout's answer.
maxLengthOnly :: Foldable t => [t a] -> [t a]
maxLengthOnly = flip filter <*> ((. length) . (==) . maximum . fmap length)
composed :: [String] -> [String]
composed = maxLengthOnly . fmap (++ "!!!")
如果无点谓词生成函数不那么笨重,那么这个maxLengthOnly
定义将是一个非常好的单行。
由于Applicative
函数实例与Monad
函数的效果相同,maxLengthOnly
也可以表示为:
maxLengthOnly = (. length) . (==) . maximum . fmap length >>= filter
(顺便说一下,您添加到问题中的split
为(>>=)
功能。)
使用Applicative
编写它的另一种方法是:
maxLengthOnly = filter <$> ((. length) . (==) . maximum . fmap length) <*> id
这看起来很像左撇子的解决方案:对于函数(,) <$> f <*> g = liftA2 (,) f g = f &&& g
而言,这并非巧合。
最后,值得注意的是,虽然最新版本的id
中的maxLengthOnly
被fmap (++ "!!!")
取代很有吸引力,但是因为{{{}}而无法工作1}}更改字符串的长度,因此会影响谓词的结果。但是,使用一个不会使谓词无效的函数,它可以很好地工作:
fmap (++ "!!!")
nicerComposed = filter
<$> ((. length) . (==) . maximum . fmap length) <*> fmap reverse
答案 2 :(得分:2)
如上所述leftaroundabout,您可以使用Arrows
来编写您的函数。但是, ghc Haskell编译器中有一个功能,它是proc
- 符号表示Arrows。它与众所周知的do
- 符号非常相似,但不幸的是,没有多少人知道它。
使用proc
- 表示法,您可以用下一个更加可更加优雅的方式编写所需的功能:
{-# LANGUAGE Arrows #-}
import Control.Arrow (returnA)
import Data.List (maximum)
composed :: [String] -> [String]
composed = proc l -> do
bangedL <- fmap (++"!!!") -< l
maxLen <- maximum . fmap length -< bangedL
returnA -< filter ((== maxLen) . length) bangedL
这可以按预期在 ghci 中使用:
ghci> composed ["alice", "bob", "david"]
["alice!!!","david!!!"]
如果您有兴趣,可以阅读一些带有漂亮图片的教程,了解什么是箭头以及这个强大的功能如何工作,这样您就可以深入了解它:
答案 3 :(得分:1)
你所拥有的本质上是一个过滤器,但是当你遍历列表时过滤功能会发生变化。我不会将其建模为“分叉”组合,而是使用以下函数f :: String -> (Int, [String])
进行折叠:
折叠完成后,您只需从元组中提取列表。
-- Not really a suitable name anymore, but...
composed :: [String] -> [String]
composed = snd . foldr f (0, [])
where f curr (maxLen, result) = let currLen = length curr
in case compare currLen maxLen of
LT -> (maxLen, result) -- drop
EQ -> (maxLen, curr:result) -- keep
GT -> (length curr, [curr]) -- reset