我想了解如何以无点方式完成以下工作:
withinBounds :: [Int] -> Bool
withinBounds xs = (all (>= 0) xs) && (all (<= 8) xs)
我理解为了可读性/理智而以这种方式编写它是优越的,但我想了解更多关于如何编写函数的信息。我一直在试着如何做到这一点。 整个(扩展?)类型签名是
[Int] -> ([Int] -> Bool) -> ([Int] -> Bool) -> (Bool -> Bool -> Bool) -> Bool
我试图去的作品的类型签名是
(a -> b) -> (a -> c) -> (b -> c -> d) -> (a -> d)
我用以混蛋 - lambda形式的笔记写下了以下内容。如果有一种方法可以在某种程度上简化lambda演算的问题,那么如果可以解释它也会很好:
\L@[] -> \f1@([] -> Bool) -> \f2@([] -> Bool) -> \f3@(Bool -> Bool -> Bool) -> f3.(f1.L).(f2.L)
在上面,.
是应用程序,@
正在捕获(因此f3是(Bool - &gt; Bool - &gt; Bool)的另一个名称)。
非常感谢。
编辑:我知道这不是最优或可重复使用的代码,而且我知道将其转换为无点可使其在可读性等方面更糟糕。为了澄清,我问我怎么能把它变成无点的因为我想了解更多关于haskell和组合的信息。
答案 0 :(得分:9)
您可以使用函数是Applicative的事实。
并以这种方式写withinBounds
:
withinBounds = pure (&&) <*> all (>= 0) <*> all (<= 8)
或者这样:
withinBounds = (&&) <$> all (>= 0) <*> all (<= 8)
的信息
答案 1 :(得分:9)
有一个类基本上专门用于具有多个“通道”的无点组合†:Arrow
。如果你决心让所有东西都没有点,那么这就是IMO的最佳选择。关于这一点的丑陋之处在于你经常需要解决这些问题:
import Control.Arrow
withinBounds = all (>=0) &&& all (<=8) >>> uncurry (&&)
如何通过图表最好地理解其工作原理:
all (>=0) ────
╱ ╲
──── &&& >>> uncurry (&&) ───
╲ ╱
all (<=8) ────
† Arrow
在广义环境中工作;不仅适用于 Hask 功能,还适用于任何合适的类别。但它足以将其应用于函数。
答案 2 :(得分:2)
围绕整个问题做一个结束,我想我可能会这样写:
import Data.Ix
withinBounds = all (inRange (0, 8))
当然,这有点过分,从那时起,人们自然会问如何以无点的方式实现inRange
。如果你绝对不能使用inRange
,那么我会以这种方式实现它:
withinBounds = all (liftA2 (&&) (>=0) (<=8))
这使用reader applicative为两个函数提供单个参数。 liftA2
是您请求的组合函数,但参数翻转:
requested :: (a -> b) -> (a -> c) -> (b -> c -> d) -> (a -> d)
liftA2 :: (b -> c -> d) -> (a -> b) -> (a -> c) -> (a -> d)
答案 3 :(得分:-2)
注意x&lt; = 8当且仅当8-x> = 0时,所以,只使用前奏,我们可以写
withinBounds :: [Int] -> Bool
withinBounds = all $ (all (>=0)) . (zipWith (+) [0,8]) . (zipWith (*) [1,-1]) . (replicate 2)
基本上我只是将x
映射到[x,x]
然后映射到[x,8-x]
,然后我要求这两个同时为&gt; = 0。
当然,正如评论中所指出的,您还可以制作a,b
参数,以便以后重复使用。
希望这有帮助。