我一直在尝试用F#编写一个程序,它接受两个值(一个元组):一个数字和一个字符串.根据字符串是否告诉程序添加或乘法,它将使用从1到该数字的所有整数(即1..n)添加或乘以输入数字.
这是代码
let addormult (n:int, what:string) =
if what = "multiply" then
let rec product n =
if n<1 then 0
else n*product(n-1)
printfn "%A" product
elif what = "sum" then
let rec sum =
if n<1 then 0
else n + sum(n-1)
printfn "%A" sum
Run Code Online (Sandbox Code Playgroud)
但是,每次我尝试运行此功能时,都会收到错误消息
"这个值不是一个功能,不能应用."
那么我做错了什么?
实际上更多的是扩展注释,但是通过使用fold
函数可以简化整个代码.
let addormult (n, what) =
let f = if what = "multiply" then (*) else (+)
List.fold f 1 [1..n]
let x = addormult(4, "multiply") // results in x = 24
let y = addormult(4, "add") // results in y = 10
Run Code Online (Sandbox Code Playgroud)
或者甚至更好,getOp
在范围之外定义,因为它通常适用.
let getOp what = if what = "multiply" then (*) else (+)
let addormult (n, what) = List.fold (getOp what) 1 [1..n]
let x = addormult(4, "multiply") // results in x = 24
let y = addormult(4, "add") // results in y = 10
Run Code Online (Sandbox Code Playgroud)
fold
也是尾递归的,确保你不会超过大N的堆栈大小限制.在F#中,很多时候你做递归时,已经有一个标准的库函数可以满足你的需要.或者如果没有,有时最好从函数中提取"一般"递归,然后根据它实现特定的东西.
现在,请注意字符串不是传达意图的最佳方式.最好使用歧视联盟来传达你的行动:
type Op = Add | Multiply
let getop = function | Multiply -> (*) | Add -> (+)
let addormult (n, what) = List.fold (getop what) 1 [1..n]
let x = addormult(4, Multiply) // results in x = 24
let y = addormult(4, Add) // results in y = 10
Run Code Online (Sandbox Code Playgroud)
这样就不会有人偶然输入"mutliply"并获得意想不到的返回值.
但实际上没有理由限制只有两个操作.使用任何操作都可以直接使用:
let doOneToN f n = List.fold f 1 [1..n]
let x0 = doOneToN (*) 4 // results in x0 = 24
let y0 = doOneToN (+) 4 // results in y0 = 10
Run Code Online (Sandbox Code Playgroud)
有了这个,您可以根据需要轻松使用部分应用程序来制作专门的功能:
let factorial = doOneToN (*) // the `n` argument is "curried"
let triangularSum = doOneToN (+) // the `n` argument is "curried"
let x1 = factorial 4 // results in x1 = 24
let y1 = triangularSum 4 // results in y1 = 10
Run Code Online (Sandbox Code Playgroud)
这是函数式编程的好处,就是它很容易混合和匹配.
当然,它很简单,你甚至可能不关心功能.呼叫let x = List.fold (*) 1 [1..4]
内联几乎更简单.功能优先的语言往往很好,也很简洁.
在这部分代码中:
let rec sum =
if n<1 then 0
else n + sum(n-1)
Run Code Online (Sandbox Code Playgroud)
sum
被定义为一个值,因为它不带参数.第三行尝试将其称为函数(sum(n-1)
),这是不可能的,因为它不是函数.
在函数中添加一个参数,例如
let rec sum x =
if x<1 then 0
else x + sum(x-1)
Run Code Online (Sandbox Code Playgroud)
在这里我取得了自由,并n
在那个功能体中取而代之x
,虽然我不知道这是不是你想做什么.但它会编译.
前缀:我不是F#专家,比我更了解的人可能能够提供更完整的答案.
这里有几件事需要解决:
let rec sum =
缺少参数变量声明.printfn "%A" product
(和sum的一个)是打印函数,而不是值.要更正此问题,您需要使用参数调用该函数.n
为两个addormult
元组值和递归值可能会导致问题-虽然我不知道如何在这样的情况下与作用域F#的交易.这是这些变化后的"乘法"版本:
let addormult (n:int, what:string) =
if what = "multiply" then
let rec product x =
if x = 1 then 1
else x * product(x-1)
printfn "product is %A" (product n)
Run Code Online (Sandbox Code Playgroud)
这是电话:
let x = addormult (4, "multiply")
Run Code Online (Sandbox Code Playgroud)
这给了我这个价值:
product is 24
Run Code Online (Sandbox Code Playgroud)