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
函数或函数参数可变,以便我可以在函数内部更改和使用它的值.
对你来说这是一个学校作业以及自己做的工作,而不只是问一个归结为"请为我做功课"的问题,这对你有好处.因为你是诚实的,所以我会给你一个比我原来更详细的答案.
首先,这似乎是一个非常奇怪的任务.使用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建议我告诉你如何使用fold
和scan
功能,我想"为什么不呢?" 请记住,这些都是高级技术,因此您可能无法获得需要使用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.fold
或Array.fold
,依次为:
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#将完成其余所有操作.