使用地图和$用于函数应用程序的Haskell eta约简

时间:2018-11-18 11:24:09

标签: haskell

嗨,我有一个任务要编写,该函数接受Int -> Int中的函数列表和单个Int中的函数,并检查应用于该Int的函数是否给出结果为大于5。

这是我的解决方案:

foo :: [Int -> Int] -> Int -> Bool
foo ls = any (>5) . f ls
     where f ls v = map ($ v) ls

我想知道有一种方法可以用eta减少最后一个参数(现在是eta减少但不是单行)用单行(不使用where或let)编写。我尝试过这样的事情:

foo ls = any (>5) . flip map ls

但是现在的问题是调用函数看起来像这样

foo [(+3), (*4), (+1)] ($2)

最后一个参数我想避免写$。我尝试过这样的事情(和类似的事情):

foo ls = any (>5) . flip map ls ($)

但是我总是会遇到某种错误。请注意,我不能更改类型签名,也不能更改翻页。

4 个答案:

答案 0 :(得分:3)

有办法吗?当然。可读吗?不能。Haskell的设计使您摆脱一些常见的错误,例如不必要的可变性或(必要的)可变状态的隐藏使用。但是,遵循其他错误的方法使之激动人心,例如以有害于可读性和可维护性的方式使代码无指向性。

关于无点定义的问题是,转换为无点定义是一个完全机械的过程(只要起始定义中不涉及模式匹配)。有一些工具可以自动执行此操作。

首先编写完全经过eta扩展的定义:

foo ls x = any (>5) (map ($ x) ls)

然后将其放入您喜欢的工具中进行转换。 http://pointfree.io/是将执行此操作的在线服务。

它吐出了完全不可读的内容:

foo = (any (> 5) .) . flip (map . flip id)

请勿使用。这不值得。

但是也许您只想缩小一个论点的范围,这太多了吗?好吧,您可以通过作弊一点,并假装第一个参数是作用域中的值而不是参数,然后在以下内容中添加以下定义来做到这一点:

foo x = any (>5) (map ($ x) ls)

它返回foo = any (> 5) . flip map ls . flip id,然后将其插入以下参数:

foo ls = any (> 5) . flip map ls . flip id

这要好一些,这要归功于(.)运算符的缺少部分。但是它仍然包含flip id,这很容易混淆。

不要这样做。保持克制。编写易于阅读和修改的代码。

答案 1 :(得分:1)

从一个完全未简化的定义开始:

foo ls v = any (>5) (map (\f -> f v) ls)

我们可以使用lambdabot拥有完全减少eta的解决方案:

foo = (any (> 5) .) . flip (map . flip id)

但是,这看起来很糟糕,不建议这样做。

答案 2 :(得分:1)

实际上,您在问题中的最后一次审判

foo ls = any (>5) . flip map ls ($)

已经非常接近解决方案。

唯一的问题是,考虑($)的定义:

($)::(a -> b) -> a -> b

($)的第一个参数是函数(a->b),但是在您的情况下,它需要一个值a,因此flip ($)交换了参数的顺序($)并重新显示为:

foo ls = any (>5) . flip map ls . flip ($)

是您想要的功能。

答案 3 :(得分:0)

由于手头有一个应用列表,您可以简单地将应用运算符<*>用作[(+3), (*4), (+1)] <*> [2]来应用它,以获得诸如[5,8,3]之类的结果列表。现在方便使用any :: Foldable t => (a -> Bool) -> t a -> Bool函数。所以在一起

checker x = any (>5) $ [(+3), (*4), (+1)] <*> [x]

应该做的工作。如果您需要的是无点版本,那么它将是

checker = any (>5) . ([(+3), (*4), (+1)] <*>) . pure