Handy F# snippets [closed]

2020-01-24 06:18发布

There are already two questions about F#/functional snippets.

However what I'm looking for here are useful snippets, little 'helper' functions that are reusable. Or obscure but nifty patterns that you can never quite remember.

Something like:

open System.IO

let rec visitor dir filter= 
    seq { yield! Directory.GetFiles(dir, filter)
          for subdir in Directory.GetDirectories(dir) do 
              yield! visitor subdir filter} 

I'd like to make this a kind of handy reference page. As such there will be no right answer, but hopefully lots of good ones.

EDIT Tomas Petricek has created a site specifically for F# snippets http://fssnip.net/.

30条回答
Ridiculous、
2楼-- · 2020-01-24 06:26

Pascal's Triangle (hey, someone might find it useful)

So we want to create a something like this:

       1
      1 1
     1 2 1
    1 3 3 1
   1 4 6 4 1

Easy enough:

let rec next = function
    | [] -> []
    | x::y::xs -> (x + y)::next (y::xs)
    | x::xs -> x::next xs

let pascal n =
    seq { 1 .. n }
    |> List.scan (fun acc _ -> next (0::acc) ) [1]

The next function returns a new list where each item[i] = item[i] + item[i + 1].

Here's the output in fsi:

> pascal 10 |> Seq.iter (printfn "%A");;
[1]
[1; 1]
[1; 2; 1]
[1; 3; 3; 1]
[1; 4; 6; 4; 1]
[1; 5; 10; 10; 5; 1]
[1; 6; 15; 20; 15; 6; 1]
[1; 7; 21; 35; 35; 21; 7; 1]
[1; 8; 28; 56; 70; 56; 28; 8; 1]
[1; 9; 36; 84; 126; 126; 84; 36; 9; 1]
[1; 10; 45; 120; 210; 252; 210; 120; 45; 10; 1]

For the adventurous, here's a tail-recursive version:

let rec next2 cont = function
    | [] -> cont []
    | x::y::xs -> next2 (fun l -> cont <| (x + y)::l ) <| y::xs
    | x::xs -> next2 (fun l -> cont <| x::l ) <| xs

let pascal2 n =
    set { 1 .. n }
    |> Seq.scan (fun acc _ -> next2 id <| 0::acc)) [1]
查看更多
Juvenile、少年°
3楼-- · 2020-01-24 06:27

Handling arguments in a command line application:

//We assume that the actual meat is already defined in function 
//    DoStuff (string -> string -> string -> unit)
let defaultOutOption = "N"
let defaultUsageOption = "Y"

let usage =  
      "Scans a folder for and outputs results.\n" +
      "Usage:\n\t MyApplication.exe FolderPath [IncludeSubfolders (Y/N) : default=" + 
      defaultUsageOption + "] [OutputToFile (Y/N): default=" + defaultOutOption + "]"

let HandlArgs arr = 
    match arr with
        | [|d;u;o|] -> DoStuff d u o
        | [|d;u|] -> DoStuff d u defaultOutOption 
        | [|d|] -> DoStuff d defaultUsageOption defaultOutOption 
        | _ ->  
            printf "%s" usage
            Console.ReadLine() |> ignore

[<EntryPoint>]
let main (args : string array) = 
    args |> HandlArgs
    0

(I had a vague memory of this technique being inspired by Robert Pickering, but can't find a reference now)

查看更多
我想做一个坏孩纸
4楼-- · 2020-01-24 06:28

OK, this has nothing to do with snippets, but I keep forgetting this:

If you are in the interactive window, you hit F7 to jump back to the code window (without deselecting the code which you just ran...)

Going from code window to F# window (and also to open the F# window) is Ctrl Alt F

(unless CodeRush has stolen your bindings...)

查看更多
够拽才男人
5楼-- · 2020-01-24 06:29

Infix Operator

I got this from http://sandersn.com/blog//index.php/2009/10/22/infix-function-trick-for-f go to that page for more details.

If you know Haskell, you might find yourself missing infix sugar in F#:

// standard Haskell call has function first, then args just like F#. So obviously
// here there is a function that takes two strings: string -> string -> string 
startsWith "kevin" "k"

//Haskell infix operator via backQuotes. Sometimes makes a function read better.
"kevin" `startsWith` "K" 

While F# doesn't have a true 'infix' operator, the same thing can be accomplished almost as elegantly via a pipeline and a 'backpipeline' (who knew of such a thing??)

// F# 'infix' trick via pipelines
"kevin" |> startsWith <| "K"
查看更多
The star\"
6楼-- · 2020-01-24 06:29

For performance intensive stuff where you need to check for null

let inline isNull o = System.Object.ReferenceEquals(o, null)
if isNull o then ... else ...

Is about 20x faster then

if o = null then ... else ...
查看更多
混吃等死
7楼-- · 2020-01-24 06:30

Weighted sum of arrays

Calculating a weighted [n-array] sum of a [k-array of n-arrays] of numbers, based on a [k-array] of weights

(Copied from this question, and kvb's answer)

Given these arrays

let weights = [|0.6;0.3;0.1|]

let arrs = [| [|0.0453;0.065345;0.07566;1.562;356.6|] ; 
           [|0.0873;0.075565;0.07666;1.562222;3.66|] ; 
           [|0.06753;0.075675;0.04566;1.452;3.4556|] |]

We want a weighted sum (by column), given that both dimensions of the arrays can be variable.

Array.map2 (fun w -> Array.map ((*) w)) weights arrs 
|> Array.reduce (Array.map2 (+))

First line: Partial application of the first Array.map2 function to weights yields a new function (Array.map ((*) weight) which is applied (for each weight) to each array in arr.

Second line: Array.reduce is like fold, except it starts on the second value and uses the first as the initial 'state'. In this case each value is a 'line' of our array of arrays. So applying an Array.map2 (+) on the first two lines means that we sum the first two arrays, which leaves us with a new array, which we then (Array.reduce) sum again onto the next (in this case last) array.

Result:

[|0.060123; 0.069444; 0.07296; 1.5510666; 215.40356|]
查看更多
登录 后发表回答