Шпаргалка по F#

08.08.2014 at 07:13

Начал изучать F# и решил набросать шпаргалку. Буду дополнять по мере изучения.

Базовый синтаксис

Отступы значимы. Причем отступы должны быть пробельными — табуляция не подойдет.
Определить переменную:

let sampleInteger = 176

Определить функцию:

let func1 x = x*x + 3  

Функции могут быть вложенными.
Функция всегда возвращает результат последнего вычисления.

let add3ints x y z = 
let add2ints x y = 
    x + y
  add2ints x (add2ints y z)

System.Console.WriteLine(add3ints 1 2 3)
System.Console.ReadLine()

Операции со списками

Создание списка:

let mylist = [1; 2; 3; 4; 5]

Получение нового списка путем преобразования старого:

let multipliedList = List.map (fun x -> x * 3) mylist

Фильтрация списка:

let biglist = List.filter (fun x -> x > 7) multipliedList

Оператор |> позволяет использовать функции в Linq стиле. Он передает выражение слева последним аргументом в выражение справа. На примере будет понятнее:

let biglist2 = 
  [1; 2; 3; 4; 5]
  |> List.map (fun x -> x * 3)
  |> List.filter (fun x -> x > 7)

Конкатенация списков:

let newlist = [1] @ [4..5]

Сворачивание списков:

let newlist = [1 .. 3]
let sumofsquares = 
  newlist
  |> List.reduce (fun acc x -> acc + x * x)

Списки являются однонаправленными, т.е. получение первого элемента намного быстрее, чем получение последнего. Если вам нужен последний элемент списка — скорее всего нужно пересмотреть алгоритм.

Рекурсия

Для того, чтобы функция была рекурсивной, нужно в объявление функции добавить ключевое слово rec

let rec fib n = 
  if (n < 3)
  then
    1
  else
    fib(n-1) + fib(n-2)

F# оптимизирует хвостовую рекурсию. Нужно стараться делать рекурсию хвостовой.
Оптимизация хвостовой рекурсии по умолчанию отключена при сборке в Debug.

Параллельные вычисления

open System.Net
open Microsoft.FSharp.Control.WebExtensions

let urlList = [ "Microsoft.com", "http://www.microsoft.com/"
                "MSDN", "http://msdn.microsoft.com/"
                "Bing", "http://www.bing.com"
              ]

let fetchAsync(name, url:string) =
  async { 
    try
      printfn "Start reading %s" name
      let uri = newSystem.Uri(url)
      let webClient = newWebClient()
      webClient.Proxy<- newWebProxy("localhost", 3128)
      let! html = webClient.AsyncDownloadString(uri)
      printfn "Read %d characters for %s"html.Length name
    with
      | ex ->printfn"%s" (ex.Message);
  }

let runAll() =
  urlList
    |>Seq.mapfetchAsync
    |>Async.Parallel
    |>Async.RunSynchronously
    |>ignore

runAll()
printfn "Hello from main thread"

let! – отличается от обычного let тем, что не останавливает текущий поток во время выполнения.
async{} – асинхронное выражение.
Async.Parallel – объединяет последовательность асинхронных выражений для параллельного выполнения.
Async.RunSynchronously – запускает параллельное выполнение и ожидает завершения.
В результате загрузка каждого урла происходит в отдельном потоке.
Вывод в консоль:

Start rSStart reading tart reading MSDN
eading Microsoft.com
Bing
Read 40370 characters for Bing
Read 1020 characters for Microsoft.com
Read 25059 characters for MSDN
Hello from main thread

Использование кода на C# из F#

Код на C#:

namespace MyClasses
{
  public class Class1
  {
    public int Id { get; set; }
    public string Name { get; set; }
    public virtual string Title { get { return "Class1 " + this.Name; } }
  }
  public class Class2 : Class1
  {
    public override string Title { get { return "Class2 " + this.Name; } }
  }
}

Использование на F#:

open MyClasses

let q1 = new Class1()
q1.Name <- "qwerty"
let q2 = new Class2()
q2.Name <- "qwerty"

System.Console.WriteLine(q1.Title)
System.Console.WriteLine(q2.Title)
System.Console.ReadLine()

Строгая типизация

В F# нет неявного преобразования между типами.
Конвертация типов:

let int10 = int 10.0
let float10 = float 10

Тип функции и тип аргумента:

let f1 (x:int) :float = float x

Приведение типов:
:> — приведение к предку
:?> — приведение к потомку

Использование кода на F# из C#

Создаем F# Library, в созданном файле пишем такой код:

namespace Library1
module IntFunctions=   
  let private add_ints x y = x + y
  let addints x y = add_ints x y

Здесь объявлены 2 функции: одна приватная и одна публичная.
Подключаем эту библиотеку к проекту на C# и используем:

using System;
using Library1;

namespace ConsoleApplication22
{
  classProgram
  {
    staticvoid Main(string[] args)
    {
      var q = IntFunctions.addints(3, 4);
      Console.WriteLine(q.ToString());
      Console.ReadLine();
    }
  }
}

Если перейти к определению класса IntFunctions, то увидим следующее:

using System;

namespace Library1
{
  public static class IntFunctions
  {
    public static int addints(int x, int y);
  }
}

Модуль виден как статический класс, функция видна как статический метод.

Pattern Matching

Сопоставление с образцом. Выражение по очереди подставляется в каждый из вариантов. Если есть совпадение – возвращается соответствующий результат.

let rec fibn =
  match n with
    | 0 -> 0
    | 1 -> 1
    | _ ->fib (n - 1) + fib (n - 2)

Для образцов можно задавать условные ограничения:

let sign x = 
match x with
    | 0 -> 0
    | x when x < 0-> -1
    | x -> 1

Каррирование

Определение с википедии:

Каррирование или карринг (англ. currying) в информатике — преобразование функции от многих аргументов в функцию, берущую свои аргументы по одному.

Все функции в F# каррированы.
Рассмотрим функцию:

let add x y = x + y

ее тип x:int ->y:int ->int
Функциям в F# не обязательно передавать все аргументы сразу. Аргументы они принимают по очереди. Например:

let add3 = add 3

Тип этой функции int ->int

System.Console.WriteLine(add3 4); // выведет 7