F#:如何使用Argument Byref Int调用函数

Nul*_*lle 2 int f# function mutable byref

我有这个代码:

let sumfunc(n: int byref) =
  let mutable s = 0 
  while n >= 1 do
    s <- n + (n-1)
    n <- n-1
  printfn "%i" s

sumfunc 6
Run Code Online (Sandbox Code Playgroud)

我收到错误:

(8,10): error FS0001: This expression was expected to have type
    'byref<int>'
but here has type
    'int'
Run Code Online (Sandbox Code Playgroud)

所以我可以告诉我这是什么问题,但我不知道如何解决它.我想我需要指定数字6是byref<int>某种方式.我只是不知道如何.我的主要目标是使n函数或函数参数可变,以便我可以在函数内部更改和使用它的值.

rmu*_*unn 6

对你来说这是一个学校作业以及自己做的工作,而不只是问一个归结为"请为我做功课"的问题,这对你有好处.因为你是诚实的,所以我会给你一个比我原来更详细的答案.

首先,这似乎是一个非常奇怪的任务.使用while循环和单个局部变量会引导您重新使用n参数,这是一个非常糟糕的主意.作为一般规则,函数永远不应修改自身之外的值 - 这就是您尝试使用byref参数执行的操作.一旦你经历了足够知道为什么byref是一个坏主意的大部分时间,你足够的经验来知道它为什么 - MIGHT -需要一些时间.但是,让我通过使用s952163编写的代码向您展示为什么这是一个坏主意:

let sumfunc2 (n: int byref)  =
    let mutable s = 0
    while n >=  1 do
        s <- n + (n - 1)
        n <- n-1
        printfn "%i" s

let t = ref 6
printfn "The value of t is %d" t.contents
sumfunc t
printfn "The value of t is %d" t.contents
Run Code Online (Sandbox Code Playgroud)

这输出:

The value of t is 7
13
11
9
7
5
3
1
The value of t is 0
Run Code Online (Sandbox Code Playgroud)

你在期待吗?您是否t因为将其传递给函数而期望更改的值?你不应该.你真的,真的不应该.函数应尽可能"纯粹" - 在编程术语中的"纯"函数,不会修改自身之外的任何东西 - 因此,如果使用相同的输入运行它两次,它应该每次产生相同的输出.

我给你一个方法来尽快解决这个问题,但我会寄我到目前为止已经写的,现在让你看到它.

更新:现在,这是一个更好的解决方法.首先,您的老师是否接受过递归?如果他没有,那么这里有一个简短的总结:函数可以调用自己,这是解决各种问题的非常有用的技术.如果您正在编写递归函数,则需要在rec之后立即添加关键字let,如下所示:

let rec sumExampleFromStackOverflow n =
    if n <= 0 then
        0
    else
        n + sumExampleFromStackOverflow (n-1)

let t = 7
printfn "The value of t is %d" t
printfn "The sum of 1 through t is %d" (sumExampleFromStackOverflow t)
printfn "The value of t is %d" t
Run Code Online (Sandbox Code Playgroud)

注意t这次我不需要做出多变.事实上,我本来可以打电话sumExampleFromStackOverflow 7,它会起作用.

现在,这不使用while循环,因此它可能不是您的老师正在寻找的.我看到s952163刚刚用不同的解决方案更新了他的答案.但是你应该尽快习惯递归的想法,因为使用递归将问题分解为单个步骤是解决F#中许多问题的一种非常强大的技术.所以尽管这不是你现在正在寻找的答案,但这你很快就会寻找的答案.

PS如果你使用了这里的任何帮助,请告诉你的老师你已经这样做了,并给他这个问题的URL(http://stackoverflow.com/questions/39698430/f-how-to-call-a-function-with-argument-byref-int),这样他就可以阅读你的问题和其他人告诉你的内容.如果他是一位好老师,他不会因为这样做而降低你的成绩; 事实上,他可能因为你如何解决问题而诚实地提出这个问题.但如果你得到了家庭作业的帮助而你没有告诉老师,1)那是不诚实的,2)从长远来看,你只会伤害自己,因为他会认为你明白了一个你可能已经拥有的概念我还明白.

更新2:s952163建议我告诉你如何使用foldscan功能,我想"为什么不呢?" 请记住,这些都是高级技术,因此您可能无法获得需要使用fold一段时间的任务.但fold基本上是一种采用任何列表并进行计算的方法,以一般方式将列表转换为单个值.使用时fold,您可以指定三个内容:要使用的列表,计算的起始值以及将执行计算的一个步骤的两个参数的函数.例如,如果您尝试将所有数字从1加起来n,那么您的"一步"功能就是let add a b = a + b.(F#中有一个更高级的功能,我正在跳过这个解释,因为你应该一次只学习一件事.通过跳过它,它可以使add函数简单易懂.)

您使用的方式fold如下所示:

let sumWithFold n =
    let upToN = [1..n]  // This is the list [1; 2; 3; ...; n]
    let add a b = a + b
    List.fold add 0 upToN
Run Code Online (Sandbox Code Playgroud)

请注意,我写道List.fold.如果upToN是一个数组,那么我会Array.fold改为编写.该参数fold,无论是List.foldArray.fold,依次为:

  1. 执行计算的一个步骤的功能
  2. 计算的初始值
  3. List.fold使用Array.fold的计算列表(如果使用)或数组(如果使用).

让我一步一步了解一下List.fold.我们假装你用4的值来调用你的函数n.

第一步:列表是[1;2;3;4],并且内部valueSoFar变量inside List.fold被设置为初始值,在我们的例子中是0.

Next:计算函数(在我们的例子中,add)valueSoFar作为第一个参数调用,列表的第一个项作为第二个参数.所以我们调用add 0 1并得到结果1.内部valueSoFar变量更新为1,列表的其余部分为[2;3;4].由于尚未空,List.fold将继续运行.

Next:add使用valueSoFar第一个参数调用计算函数(),并将列表其余部分的第一个项作为第二个参数.所以我们调用add 1 2并得到结果3.内部valueSoFar变量更新为3,列表的其余部分为[3;4].由于尚未空,List.fold将继续运行.

Next:add使用valueSoFar第一个参数调用计算函数(),并将列表其余部分的第一个项作为第二个参数.所以我们调用add 3 3并得到结果6.内部valueSoFar变量更新为6,列表的其余部分是[4](这是一个包含一个项目的列表,数字4).由于尚未空,List.fold将继续运行.

Next:add使用valueSoFar第一个参数调用计算函数(),并将列表其余部分的第一个项作为第二个参数.所以我们调用add 6 4并得到结果10.内部valueSoFar变量更新为10,列表的其余部分是[](这是一个空列表).由于列表的其余部分现在为空,List.fold将停止,并返回当前值valueSoFar作为其最终结果.

所以召唤List.fold add 0 [1;2;3;4]将基本上返回0+1+2+3+4,或10.

现在我们来谈谈scan.该scan函数就像fold函数一样,除了不返回最终值,它返回所有步骤(包括初始值)生成的值的列表.(或者,如果您调用Array.scan它,它将返回在所有步骤中生成的值的数组).换句话说,如果你打电话List.scan add 0 [1;2;3;4],它会经历相同的步骤List.fold add 0 [1;2;3;4],但它会在计算的每一步都建立一个结果列表,然后返回[0;1;3;6;10].(初始值是列表的第一项,然后是计算的每一步).

正如我所说,这些是高级功能,您的老师将不会覆盖它.但我认为我对你能做什么感兴趣.通过使用List.fold,您不必编写while循环,for循环,甚至使用递归:所有这些都是为您完成的!您所要做的就是编写一个执行计算步骤的函数,F#将完成其余所有操作.