将布尔函数列表应用于 Haskell 中的两个参数
Apply a list of Boolean functions to two arguments in Haskell
我有一个 a -> b -> Bool
类型的函数列表,我正在尝试将它们应用于两个输入并将结果与 All
或 Any
组合。我有这个与一个变量的函数一起工作:
mconcat (map (All . ) [(<7),(>7),(==7)]) $ 6
但我不知道如何对两个可变函数执行相同的操作。
这个有效:
mconcat (map (All . ) (map (uncurry) [(<),(>),(==)])) $ (,) 6 7
但在我看来这是一个丑陋的解决方法。
有更好的方法吗?
这是一种自行编写的代码 - 您只需将类型连接起来即可。但是有一些标准工具(即 Applicative
和 Traversable
)可以用来缩短代码。
在 Data.Traversable
模块中生活 sequenceA :: (Traversable t, Applicative f) => t (f a) -> f (t a)
。当专用于 []
的 Traversable
实例和应用函数 (->) r
时,我们得到:
sequenceA :: [r -> a] -> r -> [a]
因此 sequenceA
从 []
中抽取 ->
,将列表中的每个函数应用于固定参数。
sequenceA [(< 7), (> 7), (== 7)] :: (Num n, Ord n) => n -> [Bool]
-- equivalent to:
\n -> map ($ n) [(< 7), (> 7), (== 7)]
所以你的第一个函数可以写成
f :: (Num n, Ord n) => n -> Bool
f = and . sequenceA [(< 7), (> 7), (== 7)]
我正在使用 and
而不是 mconcat . map (All .)
。
对于您的第二个功能,uncurry
是正确的工具。我们必须将 uncurry
映射到二进制函数列表上以获得元组的一元函数列表,以便我们可以使用 sequenceA
提升单个参数。因为traverse f = sequenceA . map f
我们可以写成:
g :: Ord n => n -> n -> Bool
g = curry $ and . traverse uncurry [(<), (>), (==)]
(注意,对于 Ord
的任何 correctly-implemented 实例,>
和 <
应该是互斥的。所以这两个函数总是 return False
.)
原始代码的接近替代:
mconcat (map (\f a b -> All (f a b)) [(<),(<=)]) 3 4
可以进一步以毫无意义的风格重写 \f a b -> All (f a b)
:
mconcat (map ((.) (All .)) [(<),(<=)]) 3 4
我有一个 a -> b -> Bool
类型的函数列表,我正在尝试将它们应用于两个输入并将结果与 All
或 Any
组合。我有这个与一个变量的函数一起工作:
mconcat (map (All . ) [(<7),(>7),(==7)]) $ 6
但我不知道如何对两个可变函数执行相同的操作。
这个有效:
mconcat (map (All . ) (map (uncurry) [(<),(>),(==)])) $ (,) 6 7
但在我看来这是一个丑陋的解决方法。
有更好的方法吗?
这是一种自行编写的代码 - 您只需将类型连接起来即可。但是有一些标准工具(即 Applicative
和 Traversable
)可以用来缩短代码。
在 Data.Traversable
模块中生活 sequenceA :: (Traversable t, Applicative f) => t (f a) -> f (t a)
。当专用于 []
的 Traversable
实例和应用函数 (->) r
时,我们得到:
sequenceA :: [r -> a] -> r -> [a]
因此 sequenceA
从 []
中抽取 ->
,将列表中的每个函数应用于固定参数。
sequenceA [(< 7), (> 7), (== 7)] :: (Num n, Ord n) => n -> [Bool]
-- equivalent to:
\n -> map ($ n) [(< 7), (> 7), (== 7)]
所以你的第一个函数可以写成
f :: (Num n, Ord n) => n -> Bool
f = and . sequenceA [(< 7), (> 7), (== 7)]
我正在使用 and
而不是 mconcat . map (All .)
。
对于您的第二个功能,uncurry
是正确的工具。我们必须将 uncurry
映射到二进制函数列表上以获得元组的一元函数列表,以便我们可以使用 sequenceA
提升单个参数。因为traverse f = sequenceA . map f
我们可以写成:
g :: Ord n => n -> n -> Bool
g = curry $ and . traverse uncurry [(<), (>), (==)]
(注意,对于 Ord
的任何 correctly-implemented 实例,>
和 <
应该是互斥的。所以这两个函数总是 return False
.)
原始代码的接近替代:
mconcat (map (\f a b -> All (f a b)) [(<),(<=)]) 3 4
可以进一步以毫无意义的风格重写 \f a b -> All (f a b)
:
mconcat (map ((.) (All .)) [(<),(<=)]) 3 4