[Закрываются] удобные отрывки F#

Предположение в комментариях выше оказалось верным.

ОБНОВЛЕНИЕ выполняло триггер, который в свою очередь имел ссылку на хранимую процедуру, которая больше не существует.

66
задан 4 revs 23 May 2017 в 12:10
поделиться

19 ответов

Транспонирование списка (видно из в блоге Джомо Фишера )

///Given list of 'rows', returns list of 'columns' 
let rec transpose lst =
    match lst with
    | (_::_)::_ -> List.map List.head lst :: transpose (List.map List.tail lst)
    | _         -> []

transpose [[1;2;3];[4;5;6];[7;8;9]] // returns [[1;4;7];[2;5;8];[3;6;9]]

А вот хвосто-рекурсивная версия, которая (из моего схематичного профилирования) немного медленнее , но имеет то преимущество, что не выбрасывает переполнение стека, когда внутренние списки длиннее, чем 10000 элементов (на моей машине):

let transposeTR lst =
  let rec inner acc lst = 
    match lst with
    | (_::_)::_ -> inner (List.map List.head lst :: acc) (List.map List.tail lst)
    | _         -> List.rev acc
  inner [] lst

Если бы я был умен, я бы попытался распараллелить его с асинхронным ...

6
ответ дан 24 November 2019 в 14:46
поделиться

Простые операции чтения и записи в текстовые файлы

Они тривиальны, но делают доступ к файлам доступным:

open System.IO
let fileread f = File.ReadAllText(f)
let filewrite f s = File.WriteAllText(f, s)
let filereadlines f = File.ReadAllLines(f)
let filewritelines f ar = File.WriteAllLines(f, ar)

Итак

let replace f (r:string) (s:string) = s.Replace(f, r)

"C:\\Test.txt" |>
    fileread |>
    replace "teh" "the" |>
    filewrite "C:\\Test.txt"

И объединяют это с посетителем, процитированным в вопросе:

let filereplace find repl path = 
    path |> fileread |> replace find repl |> filewrite path

let recurseReplace root filter find repl = 
    visitor root filter |> Seq.iter (filereplace find repl)

Обновление Небольшое улучшение, если вы хотите иметь возможность читать «заблокированные» файлы (например, CSV-файлы, которые уже открыты в Excel ...):

let safereadall f = 
   use fs = new FileStream(f, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)
   use sr = new StreamReader(fs, System.Text.Encoding.Default)
   sr.ReadToEnd()

let split sep (s:string) = System.Text.RegularExpressions.Regex.Split(s, sep)

let fileread f = safereadall f
let filereadlines f = f |> safereadall |> split System.Environment.NewLine  
18
ответ дан 24 November 2019 в 14:46
поделиться

Удобная функция кеширования , которая хранит до макс (ключ, считыватель (ключ)) в словаре и использует SortedList для отслеживания ключей MRU

let Cache (reader: 'key -> 'value) max = 
        let cache = new Dictionary<'key,LinkedListNode<'key * 'value>>()
        let keys = new LinkedList<'key * 'value>()

        fun (key : 'key) -> ( 
                              let found, value = cache.TryGetValue key
                              match found with
                              |true ->
                                  keys.Remove value
                                  keys.AddFirst value |> ignore
                                  (snd value.Value)

                              |false -> 
                                  let newValue = key,reader key
                                  let node = keys.AddFirst newValue
                                  cache.[key] <- node

                                  if (keys.Count > max) then
                                    let lastNode = keys.Last
                                    cache.Remove (fst lastNode.Value) |> ignore
                                    keys.RemoveLast() |> ignore

                                  (snd newValue))
2
ответ дан 24 November 2019 в 14:46
поделиться

Активные шаблоны , также известные как «банановые расколы», представляют собой очень удобную конструкцию, позволяющую сопоставить несколько шаблонов регулярных выражений. Это очень похоже на AWK , но без высокой производительности DFA , потому что шаблоны сопоставляются последовательно, пока один из них не будет успешным.

#light
open System
open System.Text.RegularExpressions

let (|Test|_|) pat s =
    if (new Regex(pat)).IsMatch(s)
    then Some()
    else None

let (|Match|_|) pat s =
    let opt = RegexOptions.None
    let re = new Regex(pat,opt)
    let m = re.Match(s)
    if m.Success
    then Some(m.Groups)
    else None

Некоторые примеры использования:

let HasIndefiniteArticle = function
        | Test "(?: |^)(a|an)(?: |$)" _ -> true
        | _ -> false

type Ast =
    | IntVal of string * int
    | StringVal of string * string
    | LineNo of int
    | Goto of int

let Parse = function
    | Match "^LET\s+([A-Z])\s*=\s*(\d+)$" g ->
        IntVal( g.[1].Value, Int32.Parse(g.[2].Value) )
    | Match "^LET\s+([A-Z]\$)\s*=\s*(.*)$" g ->
        StringVal( g.[1].Value, g.[2].Value )
    | Match "^(\d+)\s*:$" g ->
        LineNo( Int32.Parse(g.[1].Value) )
    | Match "^GOTO \s*(\d+)$" g ->
        Goto( Int32.Parse(g.[1].Value) )
    | s -> failwithf "Unexpected statement: %s" s
11
ответ дан 24 November 2019 в 14:46
поделиться

Общая мемоизация , любезно предоставлена ​​ самим человеком

let memoize f = 
  let cache = System.Collections.Generic.Dictionary<_,_>(HashIdentity.Structural)
  fun x ->
    let ok, res = cache.TryGetValue(x)
    if ok then res
    else let res = f x
         cache.[x] <- res
         res

Используя это, вы можете создать кэшированный ридер следующим образом:

let cachedReader = memoize reader
24
ответ дан 24 November 2019 в 14:46
поделиться

'Unitize' функция, которая не обрабатывает единицы Использование функции FloatWithMeasure http://msdn.microsoft.com/en-us/library/ee806527 (VS.100) .aspx .

let unitize (f:float -> float) (v:float<'u>) =
  LanguagePrimitives.FloatWithMeasure<'u> (f (float v))

Пример:

[<Measure>] type m
[<Measure>] type kg

let unitize (f:float -> float) (v:float<'u>) =
  LanguagePrimitives.FloatWithMeasure<'u> (f (float v))

//this function doesn't take units
let badinc a = a + 1.

//this one does!
let goodinc v = unitize badinc v

goodinc 3.<m>
goodinc 3.<kg>

OLD версия :

let unitize (f:float -> float) (v:float<'u>) =
  let unit = box 1. :?> float<'u>
  unit * (f (v/unit))

Престижность kvb

8
ответ дан 24 November 2019 в 14:46
поделиться

Хорошо, это не имеет ничего общего с фрагментами, но я все время забываю об этом:

Если вы находитесь в интерактивном окне, вы нажимаете F7 , чтобы вернуться к окно кода (без отмены выбора кода, который вы только что запустили ...)

Для перехода из окна кода в окно F # (а также для открытия окна F #) используется Ctrl Alt F

] (если CodeRush не украл ваши привязки ...)

3
ответ дан 24 November 2019 в 14:46
поделиться

Сопоставление регулярных выражений в стиле Perl

let (=~) input pattern =
    System.Text.RegularExpressions.Regex.IsMatch(input, pattern)

Позволяет сопоставить текст, используя нотацию let test = "monkey" = ~ "monk. +" .

27
ответ дан 24 November 2019 в 14:46
поделиться

Naive CSV reader (i.e., won't handle anything nasty)

(Using filereadlines and List.transpose from other answers here)

///Given a file path, returns a List of row lists
let ReadCSV = 
        filereadlines
            >> Array.map ( fun line -> line.Split([|',';';'|]) |> List.ofArray )
            >> Array.toList

///takes list of col ids and list of rows, 
///   returns array of columns (in requested order)
let GetColumns cols rows = 
    //Create filter
    let pick cols (row:list<'a>) = List.map (fun i -> row.[i]) cols

    rows 
        |> transpose //change list of rows to list of columns
        |> pick cols      //pick out the columns we want
        |> Array.ofList  //an array output is easier to index for user

Example

"C:\MySampleCSV"
   |> ReadCSV
   |> List.tail //skip header line
   |> GetColumns [0;3;1]  //reorder columns as well, if needs be.
1
ответ дан 24 November 2019 в 14:46
поделиться

Параллельная карта

let pmap f s =
    seq { for a in s -> async { return f s } }
    |> Async.Parallel
    |> Async.Run
-2
ответ дан 24 November 2019 в 14:46
поделиться

Обработка аргументов в приложении командной строки:

//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

(Я смутно помнил, что этот метод был вдохновлен Робертом Пикерингом , но не могу найти ссылка сейчас)

2
ответ дан 24 November 2019 в 14:46
поделиться

Может быть, монада

type maybeBuilder() =
    member this.Bind(v, f) =
        match v with
        | None -> None
        | Some(x) -> f x
    member this.Delay(f) = f()
    member this.Return(v) = Some v

let maybe = maybeBuilder()

Вот краткое введение в монады для непосвященных.

8
ответ дан 24 November 2019 в 14:46
поделиться

Сортировка дерева / Сглаживание дерева в список

У меня есть следующее двоичное дерево:

             ___ 77 _
            /        \
   ______ 47 __       99
  /            \
21 _          54
    \        /  \
      43    53  74
     /
    39
   /
  32

Оно представлено следующим образом:

type 'a tree =
    | Node of 'a tree * 'a * 'a tree
    | Nil

let myTree =
    Node
      (Node
         (Node (Nil,21,Node (Node (Node (Nil,32,Nil),39,Nil),43,Nil)),47,
          Node (Node (Nil,53,Nil),54,Node (Nil,74,Nil))),77,Node (Nil,99,Nil))

Простой метод сглаживания дерева - :

let rec flatten = function
    | Nil -> []
    | Node(l, a, r) -> flatten l @ a::flatten r

Это не хвостовая рекурсия, и я считаю, что оператор @ заставляет его быть O (n log n) или O (n ^ 2) с несбалансированными двоичными деревьями. После небольшой настройки я придумал эту хвостовую рекурсивную версию O (n):

let flatten2 t =
    let rec loop acc c = function
        | Nil -> c acc
        | Node(l, a, r) ->
            loop acc (fun acc' -> loop (a::acc') c l) r
    loop [] (fun x -> x) t

Вот результат в fsi:

> flatten2 myTree;;
val it : int list = [21; 32; 39; 43; 47; 53; 54; 74; 77; 99]
5
ответ дан 24 November 2019 в 14:46
поделиться

Треугольник Паскаля (эй, кому-то это может пригодиться)

Итак, мы хотим создать что-то вроде этого:

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

Достаточно просто:

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]

следующий возвращает новый список, в котором каждый элемент [i] = элемент [i] + элемент [i + 1].

Вот результат в 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]

Для любителей приключений предлагается хвостовая рекурсивная версия:

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]
1
ответ дан 24 November 2019 в 14:46
поделиться

Создание XElements

Ничего потрясающего, но я продолжаю попасть неявное преобразование Xnames:

#r "System.Xml.Linq.dll"
open System.Xml.Linq

//No! ("type string not compatible with XName")
//let el = new XElement("MyElement", "text") 

//better
let xn s = XName.op_Implicit s
let el = new XElement(xn "MyElement", "text")

//or even
let xEl s o = new XElement(xn s, o)
let el = xEl "MyElement" "text"
2
ответ дан 24 November 2019 в 14:46
поделиться

Попарная обработка и пары

Я всегда ожидаю, что Seq.pairwise даст мне [(1,2);(3;4)], а не [(1,2);(2,3);(3,4)]. Учитывая, что в List не существует ни того, ни другого, и что мне нужно и то, и другое, вот код на будущее. Я думаю, что они хвостовые рекурсивные.

//converts to 'windowed tuples' ([1;2;3;4;5] -> [(1,2);(2,3);(3,4);(4,5)])
let pairwise lst = 
    let rec loop prev rem acc = 
       match rem with
       | hd::tl -> loop hd tl ((prev,hd)::acc)
       | _ -> List.rev acc
    loop (List.head lst) (List.tail lst) []

//converts to 'paged tuples' ([1;2;3;4;5;6] -> [(1,2);(3,4);(5,6)])    
let pairs lst = 
    let rec loop rem acc = 
       match rem with
       | l::r::tl -> loop tl ((l,r)::acc)
       | l::[] -> failwith "odd-numbered list" 
       | _ -> List.rev acc
    loop lst []
2
ответ дан 24 November 2019 в 14:46
поделиться

Взвешенная сумма массивов

Вычисление взвешенной [n-массива] суммы [k-массива из n-массивов] чисел на основе [k-массива] весов

(Скопировано из этот вопрос и kvb ответ )

Учитывая эти массивы

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|] |]

, нам нужна взвешенная сумма (по столбцам), учитывая, что оба размеры массивов могут быть переменными.

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

Первая строка : Частичное применение первой функции Array.map2 к весам дает новую функцию (Array.map ((*) weight), которая применяется (для каждого веса) к каждому массиву в arr.

Вторая строка : Array.reduce похож на fold, за исключением того, что он начинается со второго значения и использует первое как начальное «состояние». В этом случае каждое значение является «строкой» нашего массива массивов. Array.map2 (+) в первых двух строках означает, что мы суммируем первые два массива, что оставляет нам новый массив, который мы затем (Array.reduce) снова суммируем в следующий (в данном случае последний) массив.

Результат:

[|0.060123; 0.069444; 0.07296; 1.5510666; 215.40356|]
4
ответ дан 24 November 2019 в 14:46
поделиться

Тестирование производительности

(Найдено здесь и обновлено для последней версии F #)

open System
open System.Diagnostics 
module PerformanceTesting =
    let Time func =
        let stopwatch = new Stopwatch()
        stopwatch.Start()
        func()
        stopwatch.Stop()
        stopwatch.Elapsed.TotalMilliseconds

    let GetAverageTime timesToRun func = 
        Seq.initInfinite (fun _ -> (Time func))
        |> Seq.take timesToRun
        |> Seq.average

    let TimeOperation timesToRun =
        GC.Collect()
        GetAverageTime timesToRun

    let TimeOperations funcsWithName =
        let randomizer = new Random(int DateTime.Now.Ticks)
        funcsWithName
        |> Seq.sortBy (fun _ -> randomizer.Next())
        |> Seq.map (fun (name, func) -> name, (TimeOperation 100000 func))

    let TimeOperationsAFewTimes funcsWithName =
        Seq.initInfinite (fun _ -> (TimeOperations funcsWithName))
        |> Seq.take 50
        |> Seq.concat
        |> Seq.groupBy fst
        |> Seq.map (fun (name, individualResults) -> name, (individualResults |> Seq.map snd |> Seq.average))
4
ответ дан 24 November 2019 в 14:46
поделиться

F # Map <-> Словарь C #

(я знаю, я знаю, что System.Collections.Generic.Dictionary на самом деле не является словарем C #)

C # на F #

(dic :> seq<_>)                        //cast to seq of KeyValuePair
    |> Seq.map (|KeyValue|)            //convert KeyValuePairs to tuples
    |> Map.ofSeq                       //convert to Map

(от Брайана, здесь , с улучшением, предложенным Маурисио в комментарии ниже. (| KeyValue |) - активный шаблон для сопоставления KeyValuePair - из FSharp.Core - эквивалент (fun kvp - > kvp.Key, kvp.Value) )

Интересная альтернатива

Чтобы получить все неизменные качества, но со скоростью поиска O (1) словаря, вы можете использовать dict , который возвращает неизменяемый IDictionary (см. этот вопрос ).

В настоящее время я не вижу способа напрямую преобразовать словарь с помощью этого метода, кроме

(dic :> seq<_>)                        //cast to seq of KeyValuePair
    |> (fun kvp -> kvp.Key, kvp.Value) //convert KeyValuePairs to tuples
    |> dict                            //convert to immutable IDictionary

F # в C #

let dic = Dictionary()
map |> Map.iter (fun k t -> dic.Add(k, t))
dic

Что странно здесь, так это то, что FSI сообщает тип как (например):

val it : Dictionary<string,int> = dict [("a",1);("b",2)]

, но если вы вернете dict [("a", 1); ("b", 2)] обратно, FSI сообщит

IDictionary<string,int> = seq[[a,1] {Key = "a"; Value = 1; } ...
6
ответ дан 24 November 2019 в 14:46
поделиться
Другие вопросы по тегам:

Похожие вопросы: