简体   繁体   English

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

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

I have this code: 我有这个代码:

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

I get the error: 我收到错误:

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

So from that I can tell what the problem is but I just dont know how to solve it. 所以我可以告诉我这是什么问题,但我不知道如何解决它。 I guess I need to specify the number 6 to be a byref<int> somehow. 我想我需要以某种方式将数字6指定为byref<int> I just dont know how. 我只是不知道如何。 My main goal here is to make n or the function argument mutable so I can change and use its value inside the function. 我的主要目标是使n或函数参数可变,以便我可以在函数内部更改和使用它的值。

Good for you for being upfront about this being a school assignment, and for doing the work yourself instead of just asking a question that boils down to "Please do my homework for me". 对你来说这是一个学校作业以及自己做的工作,而不只是问一个归结为“请为我做功课”的问题,这对你有好处。 Because you were honest about it, I'm going to give you a more detailed answer than I would have otherwise. 因为你是诚实的,所以我会给你一个比我原来更详细的答案。

First, that seems to be a very strange assignment. 首先,这似乎是一个非常奇怪的任务。 Using a while loop and just a single local variable is leading you down the path of re-using the n parameter, which is a very bad idea . 使用while循环和单个局部变量会引导您重新使用n参数,这是一个非常糟糕的主意 As a general rule, a function should never modify values outside of itself — and that's what you're trying to do by using a byref parameter. 作为一般规则,函数永远不应修改自身之外的值 - 这就是您尝试使用byref参数执行的操作。 Once you're experienced enough to know why byref is a bad idea most of the time, you're experienced enough to know why it mightMIGHT — be necessary some of the time. 一旦你经历了足够知道为什么byref是一个坏主意的大部分时间,你经历了足够知道为什么它可能 - 可能 -需要一些时间。 But let me show you why it's a bad idea, by using the code that s952163 wrote: 但是,让我通过使用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

This outputs: 这输出:

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

Were you expecting that? 你在期待吗? Were you expecting the value of t to change just because you passed it to a function? 你是否期望t的值改变只是因为你把它传递给了一个函数? You shouldn't. 你不应该。 You really, REALLY shouldn't. 你真的,真的不应该。 Functions should, as far as possible, be "pure" -- a "pure" function, in programming terminology, is one that doesn't modify anything outside itself -- and therefore, if you run it twice with the same input, it should produce the same output every time . 函数应尽可能“纯粹” - 在编程术语中的“纯”函数,不会修改自身之外的任何东西 - 因此,如果使用相同的输入运行它两次,它应该每次产生相同的输出

I'll give you a way to solve this soon, but I'm going to post what I've written so far right now so that you see it. 我给你一个方法来尽快解决这个问题,但我会寄我到目前为止已经写的,现在让你看到它。

UPDATE : Now, here's a better way to solve it. 更新 :现在,这是一个更好的解决方法。 First, has your teacher covered recursion yet? 首先,让你的老师报道了递归吗? If he hasn't, then here's a brief summary: functions can call themselves, and that's a very useful technique for solving all sorts of problems. 如果他没有,那么这里有一个简短的总结:函数可以调用自己,这是解决各种问题的非常有用的技术。 If you're writing a recursive function, you need to add the rec keyword immediately after let , like so: 如果您正在编写递归函数,则需要在let之后立即添加rec关键字,如下所示:

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

Note how I didn't need to make t mutable this time. 注意我怎么也没必要做t可变的这段时间。 In fact, I could have just called sumExampleFromStackOverflow 7 and it would have worked. 事实上,我本来可以调用sumExampleFromStackOverflow 7并且它可以工作。

Now, this doesn't use a while loop, so it might not be what your teacher is looking for. 现在,这不使用while循环,因此它可能不是您的老师正在寻找的。 And I see that s952163 has just updated his answer with a different solution. 我看到s952163刚刚用不同的解决方案更新了他的答案。 But you should really get used to the idea of recursion as soon as you can, because breaking the problem down into individual steps using recursion is a really powerful technique for solving a lot of problems in F#. 但是你应该尽快习惯递归的想法,因为使用递归将问题分解为单个步骤是解决F#中许多问题的一种非常强大的技术。 So even though this isn't the answer you're looking for right now, it is the answer you're going to be looking for soon. 所以尽管这不是你现在正在寻找的答案,但这你很快就会寻找的答案。

PS If you use any of the help you've gotten here, tell your teacher that you've done so, and give him the URL of this question ( http://stackoverflow.com/questions/39698430/f-how-to-call-a-function-with-argument-byref-int ) so he can read what you asked and what other people told you. PS如果您使用过任何帮助,请告诉您的老师您已经这样做了,并告诉他这个问题的URL( http://stackoverflow.com/questions/39698430/f-how-to-call-a-function-with-argument-byref-int )所以他可以阅读你问的内容以及其他人告诉你的内容。 If he's a good teacher, he won't lower your grade for doing that; 如果他是一位好老师,他不会因为这样做而降低你的成绩; in fact, he might raise it for being honest and upfront about how you solved the problem. 事实上,他可能因为你如何解决问题而诚实地提出这个问题。 But if you got help with your homework and you don't tell your teacher, 1) that's dishonest, and 2) you'll only hurt yourself in the long run, because he'll think you understand a concept that you maybe haven't understood yet. 但如果你得到了家庭作业的帮助而你没有告诉老师,1)那是不诚实的,2)从长远来看,你只会伤害自己,因为他会认为你理解一个你可能已经拥有的概念我还明白。

UPDATE 2 : s952163 suggests that I show you how to use the fold and scan functions, and I thought "Why not?" 更新2 :s952163建议我告诉你如何使用foldscan功能,我想“为什么不呢?” Keep in mind that these are advanced techniques, so you probably won't get assignments where you need to use fold for a while. 请记住,这些都是高级技术,因此您可能无法在需要fold一段时间的情况下获得分配。 But fold is basically a way to take any list and do a calculation that turns the list into a single value, in a generic way. fold基本上是一种获取任何列表的方法,并以通用方式进行计算,将列表转换为单个值。 With fold , you specify three things: the list you want to work with, the starting value for your calculation, and a function of two parameters that will do one step of the calculation. 使用fold ,您可以指定三个内容:要使用的列表,计算的起始值以及将执行计算的一个步骤的两个参数的函数。 For example, if you're trying to add up all the numbers from 1 to n , your "one step" function would be let add ab = a + b . 例如,如果您尝试将所有数字从1加到n ,则“一步”函数将let add ab = a + b (There's an even more advanced feature of F# that I'm skipping in this explanation, because you should learn just one thing at a time. By skipping it, it keeps the add function simple and easy to understand.) (F#中有一个更高级的功能,我正在跳过这个解释,因为你应该一次只学习一件事。通过跳过它,它使add功能简单易懂。)

The way you would use fold looks like this: 你使用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

Note that I wrote List.fold . 请注意,我写了List.fold If upToN was an array, then I would have written Array.fold instead. 如果upToN是一个数组,那么我会编写Array.fold The arguments to fold , whether it's List.fold or Array.fold , are, in order: fold的参数,无论是List.fold还是Array.fold ,依次为:

  1. The function to do one step of your calculation 执行计算的一个步骤的功能
  2. The initial value for your calculation 计算的初始值
  3. The list (if using List.fold ) or array (if using Array.fold ) that you want to do the calculation with. 要使用的List.fold列表(如果使用List.fold )或数组(如果使用Array.fold )。

Let me step you through what List.fold does. 让我List.fold您完成List.fold工作。 We'll pretend you've called your function with 4 as the value of n . 我们会假装你已经调用4的功能价值n

First step : the list is [1;2;3;4] , and an internal valueSoFar variable inside List.fold is set to the initial value, which in our case is 0. 第一步 :该列表是[1;2;3;4]和一个内部valueSoFar内部变量List.fold被设置为初始值,在我们的情况下是0。

Next : the calculation function (in our case, add ) is called with valueSoFar as the first parameter, and the first item of the list as the second parameter. 下一步 :使用valueSoFar作为第一个参数调用计算函数(在我们的示例中为add ),并将列表的第一项作为第二个参数调用。 So we call add 0 1 and get the result 1. The internal valueSoFar variable is updated to 1, and the rest of the list is [2;3;4] . 所以我们调用add 0 1并得到结果1.内部valueSoFar变量更新为1,列表的其余部分为[2;3;4] Since that is not yet empty, List.fold will continue to run. 由于尚未为空, List.fold将继续运行。

Next : the calculation function ( add ) is called with valueSoFar as the first parameter, and the first item of the remainder of the list as the second parameter. Next :调用计算函数( add ),将valueSoFar作为第一个参数,将列表其余部分的第一项作为第二个参数调用。 So we call add 1 2 and get the result 3. The internal valueSoFar variable is updated to 3, and the rest of the list is [3;4] . 所以我们调用add 1 2并得到结果3.内部valueSoFar变量更新为3,列表的其余部分为[3;4] Since that is not yet empty, List.fold will continue to run. 由于尚未为空, List.fold将继续运行。

Next : the calculation function ( add ) is called with valueSoFar as the first parameter, and the first item of the remainder of the list as the second parameter. Next :调用计算函数( add ),将valueSoFar作为第一个参数,将列表其余部分的第一项作为第二个参数调用。 So we call add 3 3 and get the result 6. The internal valueSoFar variable is updated to 6, and the rest of the list is [4] (that's a list with one item, the number 4). 所以我们调用add 3 3并获得结果6.内部valueSoFar变量更新为6,列表的其余部分为[4] (这是一个包含一个项目的列表,数字4)。 Since that is not yet empty, List.fold will continue to run. 由于尚未为空, List.fold将继续运行。

Next : the calculation function ( add ) is called with valueSoFar as the first parameter, and the first item of the remainder of the list as the second parameter. Next :调用计算函数( add ),将valueSoFar作为第一个参数,将列表其余部分的第一项作为第二个参数调用。 So we call add 6 4 and get the result 10. The internal valueSoFar variable is updated to 10, and the rest of the list is [] (that's an empty list). 所以我们调用add 6 4并获得结果10.内部valueSoFar变量更新为10,列表的其余部分为[] (这是一个空列表)。 Since the remainder of the list is now empty, List.fold will stop, and return the current value of valueSoFar as its final result. 由于列表的其余部分现在为空, List.fold将停止,并返回valueSoFar的当前值作为其最终结果。

So calling List.fold add 0 [1;2;3;4] will essentially return 0+1+2+3+4 , or 10. 所以调用List.fold add 0 [1;2;3;4]将基本上返回0+1+2+3+4或10。

Now we'll talk about scan . 现在我们来谈谈scan The scan function is just like the fold function, except that instead of returning just the final value, it returns a list of the values produced at all the steps (including the initial value). scan函数就像fold函数一样,除了不返回最终值,它返回所有步骤(包括初始值)生成的值列表 (Or if you called Array.scan , it returns an array of the values produced at all the steps). (或者,如果您调用Array.scan ,它将返回在所有步骤中生成的值的数组)。 In other words, if you call List.scan add 0 [1;2;3;4] , it goes through the same steps as List.fold add 0 [1;2;3;4] , but it builds up a result list as it does each step of the calculation, and returns [0;1;3;6;10] . 换句话说,如果你调用List.scan add 0 [1;2;3;4] ,它将执行与List.fold add 0 [1;2;3;4]相同的步骤List.fold add 0 [1;2;3;4] ,但它会建立一个结果列出计算的每一步,并返回[0;1;3;6;10] (The initial value is the first item of the list, then each step of the calculation). (初始值是列表的第一项,然后是计算的每一步)。

As I said, these are advanced functions, that your teacher won't be covering just yet. 正如我所说,这些是高级功能,您的老师将不会覆盖它。 But I figured I'd whet your appetite for what F# can do. 但我想我会对你能做什么感兴趣。 By using List.fold , you don't have to write a while loop, or a for loop, or even use recursion: all that is done for you! 通过使用List.fold ,您不必编写while循环,或for循环,甚至使用递归:所有这些都是为您完成的! All you have to do is write a function that does one step of a calculation, and F# will do all the rest. 您所要做的就是编写一个执行计算步骤的函数,F#将完成其余所有操作。

This is such a bad idea: 这是一个糟糕的主意:

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)

Totally agree with munn's comments, here's another way to implode: 完全赞同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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM