简体   繁体   中英

How do I make a mutable argument in a function through F#?

Sorry for my question but I did not understand the answers that was related to this question so I hope someone can enlighten me further.

I am a new data science student and we are going to learn how to program in the functional language F#. We are learning about algorithms and I wanted to write the algorithms as F# functions to check if my calculations on paper were correct.

I get the following error saying:

"This value is not mutable. Consider using the mutable keyword let mutable n = expression"

My code looks like this:

let loop5( n ) =  

    let mutable x = 0

    while n > 0 do

        x <- x + 1
        n <- n + 1

    printfn "loop5(): x=%i for n=%i" x n 

loop5(4)

I'm trying to write a function looking like this (pseudocode):

loop5(n)

    x = 0

    while n > 0

         x = x + 1
         n = n + 1

    return x

Hope I made a clear question and someone can help me out here :-) Have a nice weekend

You're trying to mutate the loop's parameter n . The parameter is not mutable, so the compiler doesn't let you. That's exactly what the error tells you.

Now, normally, to make the error go away, you'd make the variable mutable. However, you can't make a function parameter mutable, so that's not an option.

Here you want to think what the meaning of your program should be. Does the loop function need to pass the updated value of n back to its caller, or is the whole mutation its internal business? If it's the former, please see @AnyMoose's answer, but from your example and explanation, I suspect it's the latter. If that is the case, simply make a mutable copy of the parameter and work with it:

let loop n' =
    let mutable x = 0
    let mutable n = n'

    ...

Separately, I want to point out that your program, as written, would actually loop indefinitely (or until it wraps around the max int value anyway), because instead of decreasing n at each step you're increasing it. If you want your program to actually finish before the next Ice Age, you need to make n decrease with each iteration:

n <- n - 1

Ref cells

Ref cells get around some of the limitations of mutables. In fact, ref cells are very simple datatypes which wrap up a mutable field in a record type. Ref cells are defined by F# as follows:

type 'a ref = { mutable contents : 'a }

The F# library contains several built-in functions and operators for working with ref cells:

let ref v = { contents = v }      (* val ref  : 'a -> 'a ref *)
let (!) r = r.contents            (* val (!)  : 'a ref -> 'a *)
let (:=) r v = r.contents <- v    (* val (:=) : 'a ref -> 'a -> unit *)

The ref function is used to create a ref cell, the ! operator is used to read the contents of a ref cell, and the := operator is used to assign a ref cell a new value. Here is a sample in fsi:

let x = ref "hello";;

val x : string ref

x;; (* returns ref instance *)
val it : string ref = {contents = "hello";}

!x;; (* returns x.contents *)
val it : string = "hello"

x := "world";; (* updates x.contents with a new value *)
val it : unit = ()

!x;; (* returns x.contents *)
val it : string = "world"

Since ref cells are allocated on the heap, they can be shared across multiple functions:

open System

let withSideEffects x =
    x := "assigned from withSideEffects function"

let refTest() =
    let msg = ref "hello"
    printfn "%s" !msg

    let setMsg() =
        msg := "world"

    setMsg()
    printfn "%s" !msg

    withSideEffects msg
    printfn "%s" !msg

let main() =
    refTest()
    Console.ReadKey(true) |> ignore

main()

The withSideEffects function has the type val withSideEffects : string ref -> unit. This program outputs the following:

hello world

Assigned from withSideEffects function

The withSideEffects function is named as such because it has a side-effect, meaning it can change the state of a variable in other functions. Ref Cells should be treated like fire. Use it cautiously when it is absolutely necessary but avoid it in general. If you find yourself using Ref Cells while translating code from C/C++, then ignore efficiency for a while and see if you can get away without Ref Cells or at worst using mutable. You would often stumble upon a more elegant and more maintanable algorithm

Aliasing Ref Cells

Note: While imperative programming uses aliasing extensively, this practice has a number of problems. In particular it makes programs hard to follow since the state of any variable can be modified at any point elsewhere in an application. Additionally, multithreaded applications sharing mutable state are difficult to reason about since one thread can potentially change the state of a variable in another thread, which can result in a number of subtle errors related to race conditions and dead locks. A ref cell is very similar to a C or C++ pointer. Its possible to point to two or more ref cells to the same memory address; changes at that memory address will change the state of all ref cells pointing to it. Conceptually, this process looks like this:

Let's say we have 3 ref cells looking at the same address in memory:

Three references to an integer with value 7

cell1, cell2, and cell3 are all pointing to the same address in memory. The .contents property of each cell is 7. Let's say, at some point in our program, we execute the code cell1 := 10, this changes the value in memory to the following:

Three references to an integer with value 10

By assigning cell1.contents a new value, the variables cell2 and cell3 were changed as well. This can be demonstrated using fsi as follows:

let cell1 = ref 7;;
val cell1 : int ref

let cell2 = cell1;;
val cell2 : int ref

let cell3 = cell2;;
val cell3 : int ref

!cell1;;
val it : int = 7

!cell2;;
val it : int = 7

!cell3;;
val it : int = 7

cell1 := 10;;
val it : unit = ()

!cell1;;
val it : int = 10

!cell2;;
val it : int = 10

!cell3;;
val it : int = 10

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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