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

F#: How to Call a function with Argument Byref Int

我有这个代码:

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 或函数参数可变,这样我就可以在函数内部更改和使用它的值。

这真是个坏主意:

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

对你坦率地说这是一项学校作业,而且你自己完成这项工作,而不是仅仅问一个归结为 "Please do my homework for me" 的问题,这对你有好处。因为你是诚实的,所以我会给你一个更详细的答案。

首先,这似乎是一个非常奇怪的任务。使用 while 循环和仅一个局部变量会导致您重新使用 n 参数,这是一个 非常糟糕的主意。作为一般规则,函数应该 永远不会 修改其自身之外的值 — 而这正是您使用 byref 参数尝试做的事情。一旦你有足够的经验知道为什么 byref 大多数时候是个坏主意,你就会有足够的经验知道为什么它 可能 可能 — 有时是必要的。但是让我用 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 的值仅仅因为您将其传递给函数而改变?你不应该。你真的,真的不应该。函数应该尽可能地是 "pure"——一个 "pure" 函数,在编程术语中,是一个不修改自身以外的任何东西的函数——因此,如果你 运行它两次使用相同的输入,它应该每次都产生 相同的输出

我很快就会给你一个解决这个问题的方法,但我会post我现在现在写下我写的东西,这样你看到了。

更新:现在,这里有一个更好的解决方法。首先,你的老师讲过递归了吗?如果他没有,那么这里有一个简短的总结:函数可以调用自己,这是解决各种问题的非常有用的技术。如果你正在写一个递归函数,你需要在 let 之后立即添加 rec 关键字,像这样:

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

let t = 7
printfn "The value of t is %d" t
printfn "The sum of 1 through t is %d" (sumExampleFromWhosebug t)
printfn "The value of t is %d" t

注意这次我不需要让 t 可变。事实上,我本可以调用 sumExampleFromWhosebug 7 并且它会起作用。

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

P.S。如果你使用了你在这里得到的任何帮助,告诉你的老师你已经这样做了,并把这个问题的 URL ( 给他,这样他就可以阅读你的问题和其他人告诉你的。如果他是一位好老师,他不会因为这样做而降低你的成绩;事实上,他可能会 提出 它是因为他诚实且坦率地说明了您是如何解决问题的。但是,如果你的家庭作业得到了帮助,但你 没有 告诉你的老师,1) 那是不诚实的,并且 2) 你只会在长期 运行 中伤害自己,因为他会认为你理解了一个你可能还没有理解的概念。

更新 2:s952163 建议我向您展示如何使用 foldscan 函数,我认为 "Why not?" 保留请记住,这些都是高级技术,因此您可能暂时不会在需要使用 fold 的地方获得作业。但是 fold 基本上是一种获取任何列表并进行计算以通用方式将列表转换为单个值的方法。使用 fold,您可以指定三项内容:要使用的列表、计算的起始值以及将执行一步计算的两个参数的函数。例如,如果您尝试将从 1 到 n 的所有数字相加,您的 "one step" 函数将是 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)。

让我向您介绍 List.fold 的作用。我们假设您调用函数时将 4 作为 n.

的值

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

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

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

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

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

所以调用 List.fold add 0 [1;2;3;4] 本质上会 return 0+1+2+3+4 或 10.

现在我们来谈谈scanscan 函数就像 fold 函数一样,除了它不是 return 只是最终值,它 return 是一个 list 在所有步骤中产生的值(包括初始值)。 (或者,如果您调用 Array.scan,它 return 是所有步骤产生的值的数组)。换句话说,如果您调用 List.scan add 0 [1;2;3;4],它会执行与 List.fold add 0 [1;2;3;4] 相同的步骤,但它会在计算的每个步骤中构建一个结果列表,并且 returns [0;1;3;6;10]。 (初始值为列表的第一项,然后每一步计算)。

正如我所说,这些是高级功能,您的老师暂时还不会介绍。但我认为我会激起您对 F# 功能的兴趣。通过使用 List.fold,您不必编写 while 循环或 for 循环,甚至不必使用递归:一切都为您完成!您所要做的就是编写一个函数来执行计算的一个步骤,F# 将完成其余所有工作。