3
votes

I'm having a problem with understanding how F# works. I come from C# and I think that I'm trying to make F# work like C#. My biggest problem is returning values in the correct format.

Example: Let's say I have function that takes a list of integers and an integer. Function should print a list of indexes where values from list match passed integer.

My code:

let indeks myList n = myList |> List.mapi (fun i x -> if x=n then i else 0);;
indeks [0..4] 3;;

However it returns:

val it : int list = [0; 0; 0; 3; 0]

instead of just [3] as I cannot ommit else in that statement. Also I have targeted signature of -> int list -> int -> int list and I get something else.

Same goes for problem no. 2 where I want to provide an integer and print every number from 0 to this integer n times (where n is the iterated value): example: MultiplyValues 3;; output: [1;2;2;3;3;3]

Best I could do was to create list of lists.

What am I missing when returning elements? How do I add nothing to the return
example: if x=n then n else AddNothingToTheReturn

2
There are a LOT of functions in the List module, and often the problem you're trying to solve has already been solved for you, as with the List.choose and List.collect functions. But if you don't know they exist, it's hard to use them! Therefore, I recommend giving fsharpforfunandprofit.com/posts/list-module-functions a read. And when you're done, read the rest of the series there. That site the best single resource I've found for learning F#. - rmunn

2 Answers

5
votes

Use List.choose:

let indeks lst n =
    lst
    |> List.mapi (fun i s -> if s = n then Some i else None)
    |> List.choose id

Sorry, I didn't notice that you had a second problem too. For that you can use List.collect:

let f (n : int) : list<int> =
    [1 .. n]
    |> List.collect (fun s -> List.init s (fun t -> s))
printfn "%A" (f 3) // [1; 2; 2; 3; 3; 3]

Please read the documentation for List.collect for more information.

EDIT

Following s952163's lead, here is another version of the first solution without the Option type:

let indeks (lst : list<int>) (n : int) : list<int> =
    lst
    |> List.fold (fun (s, t) u -> s + 1, (if u = n then (s :: t) else t)) (0, [])
    |> (snd >> List.rev)

This one traverses the original list once, and the (potentially much shorter) newly formed list once.

1
votes

The previous answer is quite idiomatic. Here's one solution that avoids the use of Option types and id:

let indeks2 lst n =
    lst
    |> List.mapi (fun i x -> (i,x))
    |> List.filter (fun x -> (fst x) % n = 0 ) 
    |> List.map snd

You can modify the filter function to match your needs.

If you plan to generate lots of sequences it might be a good idea to explore Sequence (list) comprehensions:

[for i in 1..10 do
    yield! List.replicate i i]

If statements are an expression in F# and they return a value. In this case both the IF and ELSE branch must return the same type of value. Using Some/None (Option type) gets around this. There are some cases where you can get away with just using If.