问题
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. 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.
回答1:
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:
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
回答2:
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. 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. 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 might — MIGHT — be necessary some of the time. But let me show you why it's a bad idea, by using the code that s952163 wrote:
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? 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 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. In fact, I could have just called sumExampleFromStackOverflow 7
and it would have worked.
Now, this doesn't use a while
loop, so it might not be what your teacher is looking for. And I see that s952163 has just updated his answer with a different solution. 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#. 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.
P.S. 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. 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.
UPDATE 2: s952163 suggests that I show you how to use the fold
and scan
functions, and I thought "Why not?" Keep in mind that these are advanced techniques, so you probably won't get assignments where you need to use fold
for a while. 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. 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. For example, if you're trying to add up all the numbers from 1 to n
, your "one step" function would be let add a b = 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.)
The way you would use fold
looks like this:
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
. If upToN
was an array, then I would have written Array.fold
instead. The arguments to fold
, whether it's List.fold
or Array.fold
, are, in order:
- The function to do one step of your calculation
- The initial value for your calculation
- The list (if using
List.fold
) or array (if usingArray.fold
) that you want to do the calculation with.
Let me step you through what List.fold
does. We'll pretend you've called your function with 4 as the value of 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.
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. 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]
. Since that is not yet empty, List.fold
will continue to run.
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. 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]
. Since that is not yet empty, List.fold
will continue to run.
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. 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). Since that is not yet empty, List.fold
will continue to run.
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. 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). Since the remainder of the list is now empty, List.fold
will stop, and return the current value of valueSoFar
as its final result.
So calling List.fold add 0 [1;2;3;4]
will essentially return 0+1+2+3+4
, or 10.
Now we'll talk about 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). (Or if you called Array.scan
, it returns an array of the values produced at all the steps). 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]
. (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! All you have to do is write a function that does one step of a calculation, and F# will do all the rest.
来源:https://stackoverflow.com/questions/39698430/f-how-to-call-a-function-with-argument-byref-int