F#/機能スニペットについてはすでに twoquestions があります。
ただし、ここで探しているのは、便利なスニペット、再利用可能な小さな「ヘルパー」関数です。または、はっきりとは覚えられない、あいまいだが気の利いたパターン。
何かのようなもの:
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}
これを一種の便利なリファレンスページにしたいと思います。そのため、正しい答えはありませんが、うまくいけばたくさんの良い答えがあります。
[〜#〜]編集[〜#〜]Tomas PetricekがF#スニペット専用のサイトを作成しました http://fssnip.net / 。
中置演算子
これは http://sandersn.com/blog//index.php/2009/10/22/infix-function-trick-for-f から入手しました。詳細については、そのページにアクセスしてください。
Haskellを知っている場合は、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"
F#には真の「中置」演算子はありませんが、パイプラインと「バックパイプライン」(誰がそのようなことを知っていたのですか??)を介して、ほぼ同じようにエレガントに実行できます。
// F# 'infix' trick via pipelines
"kevin" |> startsWith <| "K"
Perlスタイルの正規表現マッチング
let (=~) input pattern =
System.Text.RegularExpressions.Regex.IsMatch(input, pattern)
let test = "monkey" =~ "monk.+"
表記を使用してテキストを照合できます。
複数行の文字列
これは非常に些細なことですが、広く知られていないF#文字列の機能のようです。
let sql = "select a,b,c \
from table \
where a = 1"
これにより、次のものが生成されます。
val sql : string = "select a,b,c from table where a = 1"
F#コンパイラは、文字列リテラル内でバックスラッシュとそれに続くキャリッジリターンを検出すると、バックスラッシュから次の行の最初の非スペース文字まですべてを削除します。これにより、一連の文字列連結を使用せずに、複数行の文字列リテラルを並べることができます。
一般的なメモ化、礼儀 男 彼自身
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
テキストファイルへの単純な読み取り/書き込み
これらは些細なことですが、ファイルアクセスをパイプ可能にします。
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)
更新「ロックされた」ファイル(Excelで既に開いているcsvファイルなど)を読み取れるようにする場合は、少し改善します。
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
Nullをチェックする必要があるパフォーマンス集約型のものの場合
let inline isNull o = System.Object.ReferenceEquals(o, null)
if isNull o then ... else ...
約20倍速い
if o = null then ... else ...
アクティブパターン、別名「バナナスプリット」は、複数の正規表現パターンと1つを一致させる非常に便利な構造です。これは [〜#〜] 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
オプション合体演算子
C#のnull合体演算子??
に近い構文を持つdefaultArg
関数のバージョンが必要でした。これにより、非常に簡潔な構文を使用して、デフォルト値を指定しながらOptionから値を取得できます。
/// Option-coalescing operator - this is like the C# ?? operator, but works with
/// the Option type.
/// Warning: Unlike the C# ?? operator, the second parameter will always be
/// evaluated.
/// Example: let foo = someOption |? default
let inline (|?) value defaultValue =
defaultArg value defaultValue
/// Option-coalescing operator with delayed evaluation. The other version of
/// this operator always evaluates the default value expression. If you only
/// want to create the default value when needed, use this operator and pass
/// in a function that creates the default.
/// Example: let foo = someOption |?! (fun () -> new Default())
let inline (|?!) value f =
match value with Some x -> x | None -> f()
たぶんモナド
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()
初心者向けの monads の簡単な紹介です。
ユニットを処理しない関数を「ユニット化」します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>
旧バージョン:
let unitize (f:float -> float) (v:float<'u>) =
let unit = box 1. :?> float<'u>
unit * (f (v/unit))
kvb への称賛
スケール/比率関数ビルダー
繰り返しますが、些細なことですが、便利です。
//returns a function which will convert from a1-a2 range to b1-b2 range
let scale (a1:float<'u>, a2:float<'u>) (b1:float<'v>,b2:float<'v>) =
let m = (b2 - b1)/(a2 - a1) //gradient of line (evaluated once only..)
(fun a -> b1 + m * (a - a1))
例:
[<Measure>] type m
[<Measure>] type px
let screenSize = (0.<px>, 300.<px>)
let displayRange = (100.<m>, 200.<m>)
let scaleToScreen = scale displayRange screenSize
scaleToScreen 120.<m> //-> 60.<px>
リストの転置( Jomo Fisherのブログ で見られる)
///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
私が賢いなら、非同期で並列化しようとします...
(私は知っています、私は知っています、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)
_と同等です)
興味深い選択肢
不変の良さをすべて取得するには、DictionaryのO(1)ルックアップ速度を使用すると、不変のIDictionaryを返すdict
演算子を使用できます(-を参照)。 この質問 )。
私は現在、この方法を使用して辞書を直接変換する方法を見つけることができません。
_(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; } ...
_
LINQ-to-XMLヘルパー
namespace System.Xml.Linq
// hide warning about op_Explicit
#nowarn "77"
[<AutoOpen>]
module XmlUtils =
/// Converts a string to an XName.
let xn = XName.op_Implicit
/// Converts a string to an XNamespace.
let xmlns = XNamespace.op_Implicit
/// Gets the string value of any XObject subclass that has a Value property.
let inline xstr (x : ^a when ^a :> XObject) =
(^a : (member get_Value : unit -> string) x)
/// Gets a strongly-typed value from any XObject subclass, provided that
/// an explicit conversion to the output type has been defined.
/// (Many explicit conversions are defined on XElement and XAttribute)
/// Example: let value:int = xval foo
let inline xval (x : ^a when ^a :> XObject) : ^b =
((^a or ^b) : (static member op_Explicit : ^a -> ^b) x)
/// Dynamic lookup operator for getting an attribute value from an XElement.
/// Returns a string option, set to None if the attribute was not present.
/// Example: let value = foo?href
/// Example with default: let value = defaultArg foo?Name "<Unknown>"
let (?) (el:XElement) (name:string) =
match el.Attribute(xn name) with
| null -> None
| att -> Some(att.Value)
/// Dynamic operator for setting an attribute on an XElement.
/// Example: foo?href <- "http://www.foo.com/"
let (?<-) (el:XElement) (name:string) (value:obj) =
el.SetAttributeValue(xn name, value)
ツリーソート/ツリーをリストにフラット化
私は次の二分木を持っています:
___ 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]
パフォーマンステスト
(見つかった ここ そして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))
配列の加重和
重みの[k-array]に基づいて、数値の[n-arrayのk-array]の重み付き[n-array]合計を計算します。
これらの配列を考えると
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の各配列に。
2行目:Array.reduceはfoldに似ていますが、2番目の値から始まり、最初の値を初期の「状態」として使用する点が異なります。この場合、各値は配列の配列の「行」です。したがって、最初の2行にArray.map2(+)を適用すると、最初の2つの配列が合計され、新しい配列が残ります。次に、(Array.reduce)を次の(この場合は最後に)合計します。アレイ。
結果:
[|0.060123; 0.069444; 0.07296; 1.5510666; 215.40356|]
F#のDataSetExtensions、DataReaders
System.Data.DataSetExtensions.dll は、DataTable
をIEnumerable<DataRow>
として扱う機能を追加し、System.NullableをサポートすることでDBNull
を適切に処理する方法で個々のセルの値をボックス化解除します。たとえば、C#では、nullを含む整数列の値を取得し、非常に簡潔な構文でDBNull
をデフォルトでゼロにするように指定できます。
var total = myDataTable.AsEnumerable()
.Select(row => row.Field<int?>("MyColumn") ?? 0)
.Sum();
ただし、DataSetExtensionsが不足している領域は2つあります。まず、IDataReader
をサポートしていません。次に、F #option
タイプをサポートしていません。次のコードは両方を実行します-IDataReader
をseq<IDataRecord>
として扱うことができ、F#オプションまたはSystem.Nullableをサポートして、リーダーまたはデータセットから値をボックス化解除できます。オプション合体演算子 別の回答 と組み合わせると、DataReaderを操作するときに次のようなコードが可能になります。
let total =
myReader.AsSeq
|> Seq.map (fun row -> row.Field<int option>("MyColumn") |? 0)
|> Seq.sum
おそらく、データベースのnullを無視するより慣用的なF#の方法は...
let total =
myReader.AsSeq
|> Seq.choose (fun row -> row.Field<int option>("MyColumn"))
|> Seq.sum
さらに、以下に定義されている拡張メソッドは、F#とC#/ VBの両方から使用できます。
open System
open System.Data
open System.Reflection
open System.Runtime.CompilerServices
open Microsoft.FSharp.Collections
/// Ported from System.Data.DatasetExtensions.dll to add support for the Option type.
[<AbstractClass; Sealed>]
type private UnboxT<'a> private () =
// This class generates a converter function based on the desired output type,
// and then re-uses the converter function forever. Because the class itself is generic,
// different output types get different cached converter functions.
static let referenceField (value:obj) =
if value = null || DBNull.Value.Equals(value) then
Unchecked.defaultof<'a>
else
unbox value
static let valueField (value:obj) =
if value = null || DBNull.Value.Equals(value) then
raise <| InvalidCastException("Null cannot be converted to " + typeof<'a>.Name)
else
unbox value
static let makeConverter (target:Type) methodName =
Delegate.CreateDelegate(typeof<Converter<obj,'a>>,
typeof<UnboxT<'a>>
.GetMethod(methodName, BindingFlags.NonPublic ||| BindingFlags.Static)
.MakeGenericMethod([| target.GetGenericArguments().[0] |]))
|> unbox<Converter<obj,'a>>
|> FSharpFunc.FromConverter
static let unboxFn =
let theType = typeof<'a>
if theType.IsGenericType && not theType.IsGenericTypeDefinition then
let genericType = theType.GetGenericTypeDefinition()
if typedefof<Nullable<_>> = genericType then
makeConverter theType "NullableField"
Elif typedefof<option<_>> = genericType then
makeConverter theType "OptionField"
else
invalidOp "The only generic types supported are Option<T> and Nullable<T>."
Elif theType.IsValueType then
valueField
else
referenceField
static member private NullableField<'b when 'b : struct and 'b :> ValueType and 'b:(new:unit -> 'b)> (value:obj) =
if value = null || DBNull.Value.Equals(value) then
Nullable<_>()
else
Nullable<_>(unbox<'b> value)
static member private OptionField<'b> (value:obj) =
if value = null || DBNull.Value.Equals(value) then
None
else
Some(unbox<'b> value)
static member inline Unbox =
unboxFn
/// F# data-related extension methods.
[<AutoOpen>]
module FsDataEx =
type System.Data.IDataReader with
/// Exposes a reader's current result set as seq<IDataRecord>.
/// Reader is closed when sequence is fully enumerated.
member this.AsSeq =
seq { use reader = this
while reader.Read() do yield reader :> IDataRecord }
/// Exposes all result sets in a reader as seq<seq<IDataRecord>>.
/// Reader is closed when sequence is fully enumerated.
member this.AsMultiSeq =
let rowSeq (reader:IDataReader) =
seq { while reader.Read() do yield reader :> IDataRecord }
seq {
use reader = this
yield rowSeq reader
while reader.NextResult() do
yield rowSeq reader
}
/// Populates a new DataSet with the contents of the reader. Closes the reader after completion.
member this.ToDataSet () =
use reader = this
let dataSet = new DataSet(RemotingFormat=SerializationFormat.Binary, EnforceConstraints=false)
dataSet.Load(reader, LoadOption.OverwriteChanges, [| "" |])
dataSet
type System.Data.IDataRecord with
/// Gets a value from the record by name.
/// DBNull and null are returned as the default value for the type.
/// Supports both nullable and option types.
member this.Field<'a> (fieldName:string) =
this.[fieldName] |> UnboxT<'a>.Unbox
/// Gets a value from the record by column index.
/// DBNull and null are returned as the default value for the type.
/// Supports both nullable and option types.
member this.Field<'a> (ordinal:int) =
this.GetValue(ordinal) |> UnboxT<'a>.Unbox
type System.Data.DataRow with
/// Identical to the Field method from DatasetExtensions, but supports the F# Option type.
member this.Field2<'a> (columnName:string) =
this.[columnName] |> UnboxT<'a>.Unbox
/// Identical to the Field method from DatasetExtensions, but supports the F# Option type.
member this.Field2<'a> (columnIndex:int) =
this.[columnIndex] |> UnboxT<'a>.Unbox
/// Identical to the Field method from DatasetExtensions, but supports the F# Option type.
member this.Field2<'a> (column:DataColumn) =
this.[column] |> UnboxT<'a>.Unbox
/// Identical to the Field method from DatasetExtensions, but supports the F# Option type.
member this.Field2<'a> (columnName:string, version:DataRowVersion) =
this.[columnName, version] |> UnboxT<'a>.Unbox
/// Identical to the Field method from DatasetExtensions, but supports the F# Option type.
member this.Field2<'a> (columnIndex:int, version:DataRowVersion) =
this.[columnIndex, version] |> UnboxT<'a>.Unbox
/// Identical to the Field method from DatasetExtensions, but supports the F# Option type.
member this.Field2<'a> (column:DataColumn, version:DataRowVersion) =
this.[column, version] |> UnboxT<'a>.Unbox
/// C# data-related extension methods.
[<Extension; AbstractClass; Sealed>]
type CsDataEx private () =
/// Populates a new DataSet with the contents of the reader. Closes the reader after completion.
[<Extension>]
static member ToDataSet(this:IDataReader) =
this.ToDataSet()
/// Exposes a reader's current result set as IEnumerable{IDataRecord}.
/// Reader is closed when sequence is fully enumerated.
[<Extension>]
static member AsEnumerable(this:IDataReader) =
this.AsSeq
/// Exposes all result sets in a reader as IEnumerable{IEnumerable{IDataRecord}}.
/// Reader is closed when sequence is fully enumerated.
[<Extension>]
static member AsMultipleEnumerable(this:IDataReader) =
this.AsMultiSeq
/// Gets a value from the record by name.
/// DBNull and null are returned as the default value for the type.
/// Supports both nullable and option types.
[<Extension>]
static member Field<'T> (this:IDataRecord, fieldName:string) =
this.Field<'T>(fieldName)
/// Gets a value from the record by column index.
/// DBNull and null are returned as the default value for the type.
/// Supports both nullable and option types.
[<Extension>]
static member Field<'T> (this:IDataRecord, ordinal:int) =
this.Field<'T>(ordinal)
OK、これはスニペットとは何の関係もありませんが、私はこれを忘れ続けています:
インタラクティブウィンドウを表示している場合は、 F7 コードウィンドウに戻るには(実行したコードの選択を解除せずに...)
コードウィンドウからF#ウィンドウに移動する(そしてF#ウィンドウを開く)のは CtrlAltF
(CodeRushがバインディングを盗んだ場合を除きます...)
便利なキャッシュ関数最大max
(key,reader(key))
辞書で、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))
XElementsの作成
驚くべきことは何もありませんが、XNameの暗黙的な変換に巻き込まれ続けています。
#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"
ペアワイズとペア
私は常に、Seq.pairwiseが[(1,2);(2,3);(3,4)]ではなく[(1,2);(3; 4)]を与えることを期待しています。どちらもリストに存在せず、両方が必要だったので、後で参照できるようにコードを示します。 I thinkそれらは末尾再帰です 。
//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 []
コマンドラインアプリケーションでの引数の処理:
//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
(私はこのテクニックが Robert Pickering に触発されたという漠然とした記憶を持っていましたが、今はリファレンスを見つけることができません)
ナイーブCSVリーダー(つまり、厄介なものは何も処理しません)
(ここで他の回答からfilereadlinesとList.transposeを使用)
///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
例
"C:\MySampleCSV"
|> ReadCSV
|> List.tail //skip header line
|> GetColumns [0;3;1] //reorder columns as well, if needs be.
日付範囲
fromDate
とtoDate
の間の日付のシンプルだが便利なリスト
let getDateRange fromDate toDate =
let rec dates (fromDate:System.DateTime) (toDate:System.DateTime) =
seq {
if fromDate <= toDate then
yield fromDate
yield! dates (fromDate.AddDays(1.0)) toDate
}
dates fromDate toDate
|> List.ofSeq
パスカルの三角形(ねえ、誰かがそれが役に立つと思うかもしれません)
したがって、次のようなものを作成します。
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]
next
関数は、各item [i] = item [i] + item [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]
コードをSQLに切り替えます
このリストのほとんどよりも些細なことですが、それでも便利です。
開発中にSQLをSQL環境に移動するために、常にSQLをコードに出し入れしています。例:
let sql = "select a,b,c "
+ "from table "
+ "where a = 1"
次のように「ストリップ」する必要があります。
select a,b,c
from table
where a = 1
フォーマットを維持します。 SQLエディターのコードシンボルを削除し、SQLが機能するようになったら、手作業で元に戻すのは面倒です。これらの2つの関数は、SQLをコードからストリップに切り替えます。
// reads the file with the code quoted sql, strips code symbols, dumps to FSI
let stripForSql fileName =
File.ReadAllText(fileName)
|> (fun s -> Regex.Replace(s, "\+(\s*)\"", ""))
|> (fun s -> s.Replace("\"", ""))
|> (fun s -> Regex.Replace(s, ";$", "")) // end of line semicolons
|> (fun s -> Regex.Replace(s, "//.+", "")) // get rid of any comments
|> (fun s -> printfn "%s" s)
次に、コードソースファイルに戻す準備ができたら、次のようにします。
let prepFromSql fileName =
File.ReadAllText(fileName)
|> (fun s -> Regex.Replace(s, @"\r\n", " \"\r\n+\"")) // matches newline
|> (fun s -> Regex.Replace(s, @"\A", " \""))
|> (fun s -> Regex.Replace(s, @"\z", " \""))
|> (fun s -> printfn "%s" s)
私は 愛 入力ファイルを取り除くためにしかしそれを実現する方法を模索し始めることさえできません。誰でも?
編集:
Windowsフォームダイアログの入出力を追加することで、これらの関数のファイルの要件を排除する方法を見つけました。表示するコードが多すぎますが、そのようなことをしたい人のために、それが私がそれを解決した方法です。
リストをフラット化する
あなたがこのようなものを持っているなら:
let listList = [[1;2;3;];[4;5;6]]
そして、それを単一のリストに「フラット化」して、結果が次のようになるようにします。
[1;2;3;4;5;6]
それはこうして行うことができます:
let flatten (l: 'a list list) =
seq {
yield List.head (List.head l)
for a in l do yield! (Seq.skip 1 a)
}
|> List.ofSeq
floatのリスト内包表記
この [23.0 .. 1.0 .. 40.0]
は、サポートされているいくつかのバージョンで非推奨としてマークされました。
しかし、どうやら、これは機能します:
let dl = 9.5 / 11.
let min = 21.5 + dl
let max = 40.5 - dl
let a = [ for z in min .. dl .. max -> z ]
let b = a.Length
(ところで、浮動小数点の落とし穴があります。 fssnip --F#スニペットの他の場所で発見されました)