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

时间:2016-09-26 08:52:44

标签: function f# int 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

我收到错误:

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

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

2 个答案:

答案 0 :(得分:6)

很高兴你能够提前做好这项工作,并自己做工作,而不仅仅是问一个归结为问题的问题,请为我做好功课。#34;。因为你对此很诚实,所以我会给你一个比我原来更详细的答案。

首先,这似乎是一个非常奇怪的任务。使用while循环和单个局部变量会引导您重新使用n参数,这是 非常糟糕的主意 。作为一般规则,函数应永远修改自身之外的值 - 这是您使用byref参数尝试执行的操作。一旦您有足够的经验知道为什么byref在大多数情况下都是个坏主意,您就有足够的经验知道为什么可能 - MIGHT < / strong> - 有些时候是必要的。但是,让我通过使用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

输出:

The value of t is 7
13
11
9
7
5
3
1
The value of t is 0

你在期待吗?您是否期望t的值仅因为您将其传递给函数而发生变化?你不应该。你真的,真的不应该。功能应该尽可能纯粹&#34; - 一个纯粹的&#34;函数,在编程术语中,不会修改自身之外的任何东西 - 因此,如果使用相同的输入运行它,它应该每次都生成相同的输出。 / p>

我会尽快给你解决这个问题的方法,但是我要发布到目前为止我写的现在以便你看到它。

更新:现在,这是解决问题的更好方法。首先,让你的老师报道了递归吗?如果他还没有,那么这里有一个简短的总结:函数可以自称,并且这是解决各种问题的非常有用的技术。如果您正在编写递归函数,则需要在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

注意这次我不需要让t变成可变的。事实上,我本来可以调用sumExampleFromStackOverflow 7并且它可以工作。

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

P.S。如果您使用了此处的任何帮助,请告诉您的老师您已经这样做了,并告诉他这个问题的网址(http://stackoverflow.com/questions/39698430/f-how-to-call-a-function-with-argument-byref-int),以便他能够阅读您的要求和其他人告诉你的是什么。如果他是一位好老师,他就不会因为这样做而降低你的成绩;事实上,他可能提出它是为了诚实和前瞻性地解决问题。但如果你得到了家庭作业的帮助,而你没有告诉你的老师,1)那是不诚实的,2)你从长远来看只会伤到自己,因为他认为你理解了一个你可能尚未理解的概念。

更新2 :s952163建议我向您展示如何使用foldscan功能,我想&#34;为什么不呢?&#34;请注意,这些都是高级技术,因此您可能无法在一段时间内使用fold获取作业。但fold基本上是一种获取任何列表的方法,并以通用方式进行计算,将列表转换为单个值。使用fold,您可以指定三个内容:要使用的列表,计算的起始值以及将执行计算的一个步骤的两个参数的函数。例如,如果您尝试将所有数字从1添加到n,那么您的一步即可#34;函数将是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

请注意,我写了List.fold。如果upToN是一个数组,那么我会改为编写Array.foldfold的论据,无论是List.fold还是Array.fold,都是按顺序排列的:

  1. 执行计算一步的功能
  2. 计算的初始值
  3. 您要进行计算的列表(如果使用List.fold)或数组(如果使用Array.fold)。
  4. 让我引导您完成List.fold的工作。我们假装您使用4作为n的值来调用您的函数。

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

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

    下一步:调用计算函数(add),其中valueSoFar作为第一个参数,列表其余部分的第一项作为第二个参数。因此,我们调用add 1 2并获取结果3.内部valueSoFar变量更新为3,列表的其余部分为[3;4]。由于尚未清空,List.fold将继续运行。

    下一步:调用计算函数(add),其中valueSoFar作为第一个参数,列表其余部分的第一项作为第二个参数。因此,我们调用add 3 3并获取结果6.内部valueSoFar变量更新为6,列表的其余部分为[4](包含一个项目的列表,数字4)。由于尚未清空,List.fold将继续运行。

    下一步:调用计算函数(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。

    现在我们来谈谈scanscan函数就像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#将完成所有其他操作。

答案 1 :(得分:2)

这是一个糟糕的主意:

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

sumfunc2 (&n)

完全赞同munn的评论,这是另一种内爆的方式:

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

sumfunc3 7