简体   繁体   English

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

[英]Haskell eta reduction using map and $ for function application

Hi I have task for writing a function that takes a list of functions from Int -> Int and a single Int and checks if any function when applied to that Int gives a result which is greater than 5. 嗨,我有一个任务来编写一个函数,该函数从Int -> Int和单个Int中获取函数列表,并检查应用于该Int函数是否得出大于5的结果。

This is my solution : 这是我的解决方案:

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

I would like to know is there a way to write this in single line (without using where or let) with eta reducing last argument (now it is eta reduced but it is not in single line). 我想知道是否有一种方法可以用eta减少最后一个参数(现在是eta减少但不是单行)在单行中编写(不使用where或let)。 I tried something like this : 我尝试过这样的事情:

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

But now the problem is call of function that looks like this for it to work 但是现在的问题是调用函数看起来像这样才能正常工作

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

I would like to avoid writin $ for last argument. 我想避免在最后一个论点中写$。 I tried something like this (and similar to this) : 我尝试过这样的事情(和类似的事情):

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

But I always get some kind of error. 但是我总是会遇到一些错误。 Be aware that I must not change the type signature, thus the flip. 请注意,我不能更改类型签名,也不能更改翻转。

Is there a way? 有办法吗? Sure. 当然。 Is it readable? 可读吗? No. Haskell's design pushes you away from some common mistakes, like unnecessary mutability or hidden use of (necessary) mutable state. 不能。Haskell的设计使您摆脱一些常见的错误,例如不必要的可变性或(必要的)可变状态的隐藏使用。 But it makes it exciting to follow the path of other mistakes, like making code point-free in a manner harmful to readability and maintainability. 但是,遵循其他错误的方法也很令人兴奋,例如以有害于可读性和可维护性的方式使代码毫无意义。

The thing about point-free definitions is that converting to them is a completely mechanical process (so long as pattern matching isn't involved in the starting definition). 关于无点定义的问题是,转换为无点定义是一个完全机械的过程(只要开始定义中不涉及模式匹配)。 There are tools to do it automatically. 有一些工具可以自动执行。

Start by writing the definition fully eta-expanded: 首先编写完全eta扩展的定义:

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

Then throw it into your favorite tool for doing the conversion. 然后将其放入您喜欢的工具中进行转换。 http://pointfree.io/ is an online service that will do it. http://pointfree.io/是将执行此操作的在线服务。

And it spits out the totally unreadable: 它吐出了完全不可读的内容:

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

Don't use that. 不要使用它。 It's not worth it. 这不值得。

But maybe you only wanted to eta-contract away one argument, and this is too much? 但是也许您只想缩小一个论点的范围,这太多了吗? Well, you can do that by cheating a little bit, and pretending the first argument is a value in scope, not an argument, and throwing the following definition in: 好吧,您可以通过作弊一点,并假装第一个参数是作用域中的值而不是参数,然后在其中添加以下定义来做到这一点:

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

It returns foo = any (> 5) . flip map ls . flip id 返回foo = any (> 5) . flip map ls . flip id foo = any (> 5) . flip map ls . flip id foo = any (> 5) . flip map ls . flip id , which you then insert the argument back into: foo = any (> 5) . flip map ls . flip id ,然后将其插入回:

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

This is mildly better, thanks to lacking sections of the (.) operator. 这要好一些,这要归功于缺少(.)运算符的各个部分。 But it still has flip id in it, which is obfuscation at its simplest. 但是它仍然具有flip id ,这是最简单的混淆。

Don't do it. 不要这样 Exercise restraint. 保持克制。 Write code that's easy to read and modify. 编写易于阅读和修改的代码。

Starting with a fully non-reduced definition: 从完全非简化的定义开始:

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

We can use lambdabot to have a fully eta-reduced solution: 我们可以使用lambdabot拥有完全减少eta的解决方案:

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

However, this looks awful, and should not be recommended to do. 但是,这看起来很糟糕,不建议这样做。

Actually, your last trial in the question 其实,您在问题中的最后一次审判

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

is already very close to solution. 已经非常接近解决方案。

The only one problem is, considers the definition of ($) : 唯一的问题是,考虑($)的定义:

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

the first argument of ($) is a function (a->b) , but in your case, need be a value a , so just flip ($) swap the order of the arguments of ($) and put it back as: 的第一个参数($)是一个函数(a->b)但在你的情况下,也可以是值a ,所以才flip ($)交换的参数的顺序($)并把它早在:

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

is the function that you want. 是您想要的功能。

Since you have an applicative list at hand you may simply apply it with the applicative operator <*> as [(+3), (*4), (+1)] <*> [2] to obtain the list of results such as [5,8,3] . 由于您手头有一个应用列表,因此您可以简单地用应用运算符<*>将其应用为[(+3), (*4), (+1)] <*> [2]以获取结果列表,例如作为[5,8,3] Now it's the any :: Foldable t => (a -> Bool) -> ta -> Bool function that comes handy. 现在可以使用它了any :: Foldable t => (a -> Bool) -> ta -> Bool函数。 So all together 所以在一起

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

should do the job. 应该做的工作。 If you need the pointfree version than it would be like 如果您需要没有积分的版本,它将比

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM