经过数年的OOP,我正在尝试学习haskell。我正在阅读Happy Haskell。它提供了以下代码:
plus :: Int -> Int -> Int
plus x y = x + y
plus' :: Int -> Int -> Int
plus' = \x -> \y -> x + y
increment :: Int -> Int
increment = plus 1
increment' :: Int -> Int
increment' = (\x -> \y -> x + y) 1
我了解plus和plus'的工作原理(它们是相同的,不同的语法)。 但是增加,我不明白。
increment :: Int -> Int
表示它需要一个int并返回一个int,对吗?但是在那之后,实际的功能是:
increment = plus 1
问题:
整数值增量在哪里?在符号x
的右边是否应该有一个=
或其他东西来表示该函数作为输入的整数值?像这样:
increment _ = plus 1 x
编辑:另外,增量的定义不应该是Int -> (Int -> Int)
,因为它需要一个int
并将其传递给一个需要一个int
并返回和{{1} }?
答案 0 :(得分:5)
在Haskell中,您可以具有函数的部分应用程序。看看Haskell Wiki: Partial Application
尤其是,如果您查看任何函数的类型签名,则其输入(参数)与输出之间没有真正的区别,这是因为实际上,您的函数plus :: Int -> Int -> Int
是一个在给定给定值的情况下的函数一个Int,将返回另一个函数,该函数本身将使用其余参数并返回int:Int -> Int
。这称为部分应用
这意味着,当您调用increment = plus 1
时,您的意思是增量等于-记住部分应用程序-一个函数(由plus 1
返回),该函数本身取一个整数并返回一个整数
因为Haskell是一种函数式编程语言,所以所有具有等号的对象都不是赋值,而是更像是定义,因此理解部分应用程序的一种简单方法实际上就是遵循等号:
increment = plus 1 =
plus 1 y = 1 + y
如您所见,部分应用程序可用于定义更特定的功能,例如将1加到一个比加两个数字更具体的数字上。它还可以更多地使用无点样式,在这种情况下,您可以连接多个函数。
还请注意,使用像(+)
这样的中缀函数,您可以部分应用到左侧或右侧,例如,这对于非交换函数很有用
divBy2 :: Float -> Float
divBy2 = (/2)
div2by :: Float -> Float
div2by = (2/)
Prelude> divBy2 3
1.5
Prelude> div2by 2
1.0
答案 1 :(得分:4)
应该是[HttpGet]
public IActionResult DownloadLog()
{
var (path, bytes) = GetThePathAndTheNumberOfBytesIKnowHaveBeenFlushed();
var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); // this ensures that the file can be read while it's still being written
return new PartialFileStreamResult(stream, "text/plain", bytes);
}
,但通常increment x = plus 1 x
与foo x = bar x
是同一件事,因为如果foo = bar
是一个在每次与任何调用时返回f
的函数参数g(x)
,则x
与f
的功能相同。因此g
的效果也一样。
答案 2 :(得分:2)
这是因为Haskell中的所有函数都是隐式curried。因此,返回带有参数的函数的函数和带有两个返回值的参数的函数(两者的类型均为a -> a -> a
†)之间没有区别。因此,调用带有很少参数的plus
(或任何其他函数)只会返回一个应用了已给定参数的新函数。在大多数语言中,这将是一个参数错误。另请参见point-free style。
† Haskell类型签名是右相关的,因此a -> a -> a -> a
等效于a -> (a -> (a -> a))
。
答案 3 :(得分:2)
plus
和plus'
的示例具有启发性。您会看到后者似乎没有参数,至少在等号的左侧:
plus' :: Int -> Int -> Int
plus' = \x -> \y -> x + y
让我们制作另一对版本的增量版本(我将数字“颠簸” 1之后再命名),将其转换为您给出的最终版本的一半:
bump :: Int -> Int
bump y = 1 + y
bump' :: Int -> Int
bump' = \y -> 1 + y
这两个定义之间的类比就像plus
和plus'
之间的类比,因此它们应该是有意义的,包括后者,即使它的左侧没有形式上的论点。等号。
现在,您对bump'
的理解与您对increment'
的理解完全相同。实际上,我们将bump'
定义为等于increment'
所等于的东西。
即(稍后将看到)bump'
定义的右侧,
\y -> 1 + y
等于
plus 1
这两种符号或表达式是定义“采用数字并返回多于数字的函数”的两种语法方式。
但是什么使它们相等?!嗯,(正如其他答复者所解释的),表达式plus 1
是部分应用的。从某种意义上说,编译器知道plus
需要两个参数(毕竟是以这种方式声明的),因此,当它出现在此处仅应用于一个参数时,编译器知道它仍在等待另一个参数。它通过给您一个函数来表示“等待”,也就是说,如果再给一个参数,无论是现在还是以后,这样做都将使此功能完全适用,并且程序实际上将跳转到plus
的函数体中(因此,为给定的两个参数计算x + y
,即表达式1
的文字plus 1
和稍后给出的“一个”参数)
Haskell的乐趣和价值的关键部分是将功能本身视为事物,它们可以传递并非常灵活地从一个转换为另一个。局部应用就是将一件事(当您要确定附加值的时候,带有“太多参数”的函数)转换为“恰到好处”的函数的一种方式。您可以将部分应用的函数传递给需要特定数量参数的接口。或者,您可能只是想基于一个通用定义定义多个专用功能(因为我们可以定义通用plus
以及更具体的功能,例如plus 1
和plus 7
)。