« Fsharp » : différence entre les versions
De Banane Atomic
Aller à la navigationAller à la recherche
Aucun résumé des modifications |
(→Array) |
||
(40 versions intermédiaires par le même utilisateur non affichées) | |||
Ligne 1 : | Ligne 1 : | ||
[[Category:.NET]] | |||
= Links = | |||
* [https://learn.microsoft.com/en-us/archive/msdn-magazine/2010/april/fsharp-basics-an-introduction-to-functional-programming-for-net-developers F# Basics - An Introduction to Functional Programming for .NET Developers] | |||
= Command line = | = Command line = | ||
<kode lang='ps'> | <kode lang='ps'> | ||
Ligne 25 : | Ligne 29 : | ||
|} | |} | ||
= | = for = | ||
<kode lang='fs'> | |||
for i in [1..3] do | |||
printfn "%A" i | |||
for i = 1 to 3 do | |||
printfn "%A" i | |||
</kode> | |||
= while = | |||
<kode lang='fs'> | <kode lang='fs'> | ||
let | let mutable keepRunning = true | ||
let | while keepRunning do | ||
let | // ... | ||
keepRunning <- false | |||
</kode> | |||
= match = | |||
<kode lang='fs'> | |||
let x = 2 | |||
let result = | |||
match x with | |||
| 1 -> "one" | |||
| 2 -> "two" | |||
| 3 | 4 -> "soon implemented" // 3 or 4 | |||
| _ -> "not implemented" | |||
| y -> $"not implemented for {y}" | |||
// pattern matching function syntax | |||
let numberToWord = function | |||
| 1 -> "one" | |||
| 2 -> "two" | |||
| _ -> "not implemented" | |||
numberToWord x // "two" | |||
</kode> | |||
= String = | |||
<kode lang='fs'> | |||
let s = "abcde" | |||
// slice | |||
s[1..3] // "bcd" | |||
// triple-quote string allows to use double-quote within the string | |||
let s = """<tag attribute="value" />""" | |||
// concat strings | |||
let sb = StringBuilder() | |||
sb.Append("abc") |> ignore | |||
sb.Append("def") |> ignore | |||
</kode> | |||
= Collection = | |||
== List == | |||
Ordered, immutable, series of elements of the same type. | |||
<kode lang='fs'> | |||
let list1 = [1; 2; 3] | |||
let list2 = [1..5] // [1; 2; 3; 4; 5] | |||
let list3 = [0..2..10] // [0; 2; 4; 6; 8; 10] | |||
let list4 = [for x in 1..5 -> x*x] // [1; 4; 9; 16; 25] | |||
// elements in lists are not mutable | // elements in lists are not mutable | ||
// cons operator | |||
0 :: list1 // create a new list with 0 at the head [0; 1; 2; 3] | |||
// extract head and tail | |||
let head :: tail = list1 // head: 1, tail: [2; 3] | |||
// concatenate operator | |||
list1 @ [4; 5] // create a new list [1; 2; 3; 4; 5] | |||
List.sum(list1) // 6 | |||
List.fold (+) 0 list1 // 6 - aggregate | |||
List.iter (fun x -> printfn "%A" x) list1 // 1 2 3 | |||
</kode> | |||
== Array == | |||
Fixed-sized, zero-based, mutable collection of consecutive elements of the same type. | |||
<kode lang='fs'> | |||
let myArray = [|1; 2; 3|] | let myArray = [|1; 2; 3|] | ||
// elements in arrays are mutable | // elements in arrays are mutable | ||
myArray[1] <- 4 // [|1; 4; 3|] | myArray[1] <- 4 // [|1; 4; 3|] | ||
</kode> | |||
== Map == | |||
== Set == | |||
== Immutable Dictionary == | |||
= Composite data type = | |||
== Tuple == | |||
<kode lang='fs'> | |||
let tuple1 = (1, "one") | |||
let (a, b) = tuple1 // a: 1, b: "one" | |||
</kode> | |||
== Record == | |||
<kode lang='fs'> | |||
type Item = { Id: int; Name: string; } | |||
let item1 = { Id = 1; Name = "one" } | |||
</kode> | |||
= Function = | |||
<kode lang='fs'> | |||
let add x y = x + y | |||
add 1 2 // 3 | |||
let myFunction param1 param2 = | |||
let result = param1 + param2 | |||
result | |||
// let myFunction (param1: int) (param2: int) : int = ... | |||
myFunction 1 2 // 3 | |||
</kode> | |||
= Unit = | |||
Function which doesn't return any value. | |||
<kode lang='fs'> | |||
let myUnit (param1: int) (param2: int) = | |||
let result = param1 + param2 | |||
printfn "%i" result | |||
() // useless because printfn is already a unit | |||
myUnit 1 2 // 3 | |||
</kode> | |||
= Convertion = | |||
<kode lang='fs'> | |||
int "123" // 123 | |||
float 123 // 123.0 | |||
string 123 // "123" | |||
</kode> | </kode> | ||
Ligne 52 : | Ligne 184 : | ||
] | ] | ||
} | } | ||
</filebox> | |||
= Use binding for disposable object = | |||
<kode lang='fs'> | |||
use file = File.CreateToText("file.txt") | |||
// Dispose will be called when binding goes out of the scope | |||
</kode> | |||
= Module = | |||
Équivalent to a C# static class. | |||
<kode lang='fs'> | |||
// nested module | |||
module Math = | |||
let add x y = x + y | |||
let sub x y = x - y | |||
let v = Math.add 1 2 | |||
open Math | |||
let v = add 1 2 | |||
// top level module (no =, no indentation, in a separate file) | |||
module Math | |||
let add x y = x + y | |||
let sub x y = x - y | |||
</kode> | |||
= Module Design Pattern = | |||
Separate the definition of data and the behaviors. | |||
<filebox fn='DomainTypes.fs'> | |||
namespace Finance | |||
module DomainTypes = | |||
type Transaction = | |||
{ Id: int; Amount: float} | |||
</filebox> | |||
<filebox fn='Transaction.fs'> | |||
namespace Finance | |||
module Transaction = | |||
let create id amount = | |||
{ Id = id; Amount = amount } | |||
</filebox> | |||
= Execute a shell command = | |||
* [https://alexn.org/blog/2020/12/06/execute-shell-command-in-fsharp/ Execute shell commands in F#] | |||
* [http://blog.ctaggart.com/2013/10/running-process-in-f.html Running a Process in F#] | |||
<filebox fn='Process.fs'> | |||
module Process = | |||
let Run command args = | |||
let psi = Diagnostics.ProcessStartInfo() | |||
psi.UseShellExecute <- false | |||
psi.WindowStyle <- Diagnostics.ProcessWindowStyle.Hidden | |||
psi.RedirectStandardOutput <- true | |||
psi.RedirectStandardError <- true | |||
let stdout = Event<string>() | |||
let stderr = Event<string>() | |||
psi.FileName <- command | |||
psi.Arguments <- args | |||
// psi.WorkingDirectory | |||
let stdout = Event<string>() | |||
let stderr = Event<string>() | |||
let writeOut = printfn "%s" | |||
stdout.Publish |> Event.add writeOut | |||
stderr.Publish |> Event.add writeOut | |||
use p = new Diagnostics.Process() | |||
p.StartInfo <- psi | |||
p.OutputDataReceived |> Event.add(fun ev -> if ev.Data <> null then stdout.Trigger ev.Data) | |||
p.ErrorDataReceived.Add(fun ev -> if ev.Data <> null then stderr.Trigger ev.Data) | |||
p.Start() |> ignore | |||
p.BeginOutputReadLine() | |||
p.BeginErrorReadLine() | |||
p.WaitForExit() | |||
p.ExitCode | |||
</filebox> | |||
= Compilation order = | |||
<filebox fn='MyProject.fsproj' lang='xml'> | |||
<ItemGroup> | |||
<Compile Include="File2.fs" /> | |||
<Compile Include="File1.fs" /> | |||
</ItemGroup> | |||
</filebox> | </filebox> | ||
Dernière version du 30 juillet 2024 à 14:00
Links
Command line
dotnet fsi # start F# Interactive dotnet fsi script.fsx # run script.fsx dotnet new console -o fsharp1 -lang f# |
printf
let s = "value" printfn "text %A text" s |
Format | Type |
---|---|
%A | any |
%s | string |
%i | integer |
%b | boolean |
for
for i in [1..3] do printfn "%A" i for i = 1 to 3 do printfn "%A" i |
while
let mutable keepRunning = true while keepRunning do // ... keepRunning <- false |
match
let x = 2 let result = match x with | 1 -> "one" | 2 -> "two" | 3 | 4 -> "soon implemented" // 3 or 4 | _ -> "not implemented" | y -> $"not implemented for {y}" // pattern matching function syntax let numberToWord = function | 1 -> "one" | 2 -> "two" | _ -> "not implemented" numberToWord x // "two" |
String
let s = "abcde" // slice s[1..3] // "bcd" // triple-quote string allows to use double-quote within the string let s = """<tag attribute="value" />""" // concat strings let sb = StringBuilder() sb.Append("abc") |> ignore sb.Append("def") |> ignore |
Collection
List
Ordered, immutable, series of elements of the same type.
let list1 = [1; 2; 3] let list2 = [1..5] // [1; 2; 3; 4; 5] let list3 = [0..2..10] // [0; 2; 4; 6; 8; 10] let list4 = [for x in 1..5 -> x*x] // [1; 4; 9; 16; 25] // elements in lists are not mutable // cons operator 0 :: list1 // create a new list with 0 at the head [0; 1; 2; 3] // extract head and tail let head :: tail = list1 // head: 1, tail: [2; 3] // concatenate operator list1 @ [4; 5] // create a new list [1; 2; 3; 4; 5] List.sum(list1) // 6 List.fold (+) 0 list1 // 6 - aggregate List.iter (fun x -> printfn "%A" x) list1 // 1 2 3 |
Array
Fixed-sized, zero-based, mutable collection of consecutive elements of the same type.
let myArray = [|1; 2; 3|] // elements in arrays are mutable myArray[1] <- 4 // [|1; 4; 3|] |
Map
Set
Immutable Dictionary
Composite data type
Tuple
let tuple1 = (1, "one") let (a, b) = tuple1 // a: 1, b: "one" |
Record
type Item = { Id: int; Name: string; } let item1 = { Id = 1; Name = "one" } |
Function
let add x y = x + y add 1 2 // 3 let myFunction param1 param2 = let result = param1 + param2 result // let myFunction (param1: int) (param2: int) : int = ... myFunction 1 2 // 3 |
Unit
Function which doesn't return any value.
let myUnit (param1: int) (param2: int) = let result = param1 + param2 printfn "%i" result () // useless because printfn is already a unit myUnit 1 2 // 3 |
Convertion
int "123" // 123 float 123 // 123.0 string 123 // "123" |
Console input
printf "Enter a value: " let input = Console.ReadLine() printfn "Entered value: %s" input |
.vscode/launch.json |
{ "configurations": [ { "console": "integratedTerminal" // // switch from internalConsole to integratedTerminal to allow console input in VS code } ] } |
Use binding for disposable object
use file = File.CreateToText("file.txt") // Dispose will be called when binding goes out of the scope |
Module
Équivalent to a C# static class.
// nested module module Math = let add x y = x + y let sub x y = x - y let v = Math.add 1 2 open Math let v = add 1 2 // top level module (no =, no indentation, in a separate file) module Math let add x y = x + y let sub x y = x - y |
Module Design Pattern
Separate the definition of data and the behaviors.
DomainTypes.fs |
namespace Finance module DomainTypes = type Transaction = { Id: int; Amount: float} |
Transaction.fs |
namespace Finance module Transaction = let create id amount = { Id = id; Amount = amount } |
Execute a shell command
Process.fs |
module Process = let Run command args = let psi = Diagnostics.ProcessStartInfo() psi.UseShellExecute <- false psi.WindowStyle <- Diagnostics.ProcessWindowStyle.Hidden psi.RedirectStandardOutput <- true psi.RedirectStandardError <- true let stdout = Event<string>() let stderr = Event<string>() psi.FileName <- command psi.Arguments <- args // psi.WorkingDirectory let stdout = Event<string>() let stderr = Event<string>() let writeOut = printfn "%s" stdout.Publish |> Event.add writeOut stderr.Publish |> Event.add writeOut use p = new Diagnostics.Process() p.StartInfo <- psi p.OutputDataReceived |> Event.add(fun ev -> if ev.Data <> null then stdout.Trigger ev.Data) p.ErrorDataReceived.Add(fun ev -> if ev.Data <> null then stderr.Trigger ev.Data) p.Start() |> ignore p.BeginOutputReadLine() p.BeginErrorReadLine() p.WaitForExit() p.ExitCode |
Compilation order
MyProject.fsproj |
<ItemGroup> <Compile Include="File2.fs" /> <Compile Include="File1.fs" /> </ItemGroup> |
Dev env
- VScode
- Ionide for F# extension → intellisense
- fantomas-fmt → format F#
Create launch and tasks
- Disable the Ionide for F# extension
- Run → Run Without Debugging
- Select environment: .NET 5+ and .NET Core (this create the launch.json file)
- Open launch.json → Add Configuration → .NET: Launch .NET Core Console App
- Fill target-framework = net6.0 and project-name = myproject
- Run → Run Without Debugging
- Configure Task → Create tasks.json file from template → Select a Task Template: .NET Core