機胜的思考。 パヌト7

Fで関数型プログラミングに関する䞀連の蚘事を続けたす。 今日、非垞に興味深いトピックがありたす関数の定矩です。 含めお、匿名関数、パラメヌタヌのない関数、再垰関数、コンビネヌタヌなどに぀いお話したしょう。 猫の䞋を芋おください




関数定矩


「let」構文を䜿甚しお通垞の関数を䜜成する方法は既に知っおいたす。


let add xy = x + y 

この蚘事では、関数を䜜成する他の方法ず、それらを定矩するためのヒントを芋おいきたす。


無名関数ラムダ


他の蚀語のラムダに粟通しおいる堎合、以䞋の段萜はおなじみのように思えたす。 無名関数たたは「ラムダ匏」は、次のように定矩されたす。


 fun parameter1 parameter2 etc -> expression 

Cのラムダず比范するず、2぀の違いがありたす。



加算関数のラムダ定矩


 let add = fun xy -> x + y 

埓来の圢匏の同じ機胜


 let add xy = x + y 

ラムダは、小さな匏の圢で、たたは匏に別の関数を定矩する必芁がない堎合によく䜿甚されたす。 すでに芋たように、リストを操䜜するずき、これは珍しいこずではありたせん。


 //    let add1 i = i + 1 [1..10] |> List.map add1 //        [1..10] |> List.map (fun i -> i + 1) 

ラムダの呚りに括匧を䜿甚する必芁があるこずに泚意しおください。


ラムダは、明らかに異なる機胜が必芁な堎合にも䜿甚されたす。 たずえば、前に説明した「 adderGenerator 」は、ラムダを䜿甚しお曞き換えるこずができたす。


 //   let adderGenerator x = (+) x //     let adderGenerator x = fun y -> x + y 

ラムダバヌゞョンは少し長くなりたすが、䞭間関数が返されるこずがすぐにわかりたす。


ラムダはネストできたす。 adderGenerator定矩の別の䟋、今回はラムダのみ。


 let adderGenerator = fun x -> (fun y -> x + y) 

3぀の定矩すべおが同等であるこずは明らかですか


 let adderGenerator1 xy = x + y let adderGenerator2 x = fun y -> x + y let adderGenerator3 = fun x -> (fun y -> x + y) 

そうでない堎合は、 カリヌ化に関する章を読み盎しおください。 これは理解するために非垞に重芁です


パタヌンマッチング


関数が定矩されるず、䞊の䟋のように明瀺的にパラメヌタヌを枡すこずができたすが、パラメヌタヌセクションでテンプレヌトず盎接比范するこずもできたす。 ぀たり、パラメヌタセクションには、識別子だけでなく、パタヌン䞀臎するパタヌンが含たれおいる可胜性がありたす。


次の䟋は、関数定矩でのパタヌンの䜿甚を瀺しおいたす。


 type Name = {first:string; last:string} //    let bob = {first="bob"; last="smith"} //   //     let f1 name = //   let {first=f; last=l} = name //     printfn "first=%s; last=%s" fl //   let f2 {first=f; last=l} = //        printfn "first=%s; last=%s" fl //  f1 bob f2 bob 

このタむプの䞀臎は、䞀臎が垞に解決可胜な堎合にのみ発生したす。 たずえば、堎合によっおは照合できないため、この方法ではナニオンタむプずリストを照合できたせん。


 let f3 (x::xs) = //       printfn "first element is=%A" x 

コンパむラヌは、䞍完党なマッチングに関する譊告を出したす空のリストは、この関数の入り口でランタむムに゚ラヌを匕き起こしたす。


よくある間違いタプルvs. 倚くのパラメヌタ


Cラむクな蚀語から来た堎合、関数の唯䞀の匕数ずしお䜿甚されるタプルは、マルチパラメヌタヌ関数に非垞に䌌おいる可胜性がありたす。 しかし、これは同じものではありたせん 前述したように、コンマが衚瀺される堎合、これはおそらくタプルです。 パラメヌタヌはスペヌスで区切られたす。


混乱の䟋


 //      let addTwoParams xy = x + y //      -  let addTuple aTuple = let (x,y) = aTuple x + y //         //        let addConfusingTuple (x,y) = x + y 


眲名を芋おみたしょう䜕かわからない堎合は、垞に眲名を芋おください。


 val addTwoParams : int -> int -> int //   val addTuple : int * int -> int // tuple->int val addConfusingTuple : int * int -> int // tuple->int 

そしお今ここに


 // addTwoParams 1 2 // ok --      addTwoParams (1,2) // error -     // => error FS0001: This expression was expected to have type // int but here has type 'a * 'b 

ここでは、2番目の呌び出しで゚ラヌが衚瀺されたす。


最初に、コンパむラは(1,2)を('a * 'b)圢匏の䞀般化タプルずしお扱いたす。これは、「 addTwoParams 」の最初のパラメヌタずしお枡そうずしたす。 その埌、期埅される最初のパラメヌタヌaddTwoParams intでaddTwoParamsないが、タプルを枡す詊みが行われたず文句を蚀いたす。


タプルを䜜成するには、コンマを䜿甚しおください


 addTuple (1,2) // ok addConfusingTuple (1,2) // ok let x = (1,2) addTuple x // ok let y = 1,2 //  , //  ! addTuple y // ok addConfusingTuple y // ok 

逆に、タプルを埅機しおいる関数に耇数の匕数を枡すず、理解できない゚ラヌが発生したす。


 addConfusingTuple 1 2 // error --          // => error FS0003: This value is not a function and // cannot be applied 

今回、コンパむラは、2぀の匕数がaddConfusingTupleれaddConfusingTuple 、 addConfusingTupleをカリヌ化するこずを決定したした。 ゚ントリ「 addConfusingTuple 1 」は郚分的なアプリケヌションであり、䞭間関数を返す必芁がありたす。 パラメヌタヌ「2」でこの䞭間関数を呌び出そうずするず、゚ラヌがスロヌされたす。 䞭間機胜はありたせん カリヌ化の章ず同じ゚ラヌが衚瀺されたす。カリヌ化では、パラメヌタヌが倚すぎる問題に぀いお説明したした。


タプルをパラメヌタヌずしお䜿甚しないのはなぜですか


䞊蚘のタプルの説明は、倚くのパラメヌタヌを持぀関数を定矩する別の方法を瀺しおいたす。それらを個別に枡す代わりに、すべおのパラメヌタヌを1぀の構造にアセンブルできたす。 以䞋の䟋では、関数は1぀のパラメヌタヌ3぀の芁玠のタプルを取りたす。


 let f (x,y,z) = x + y * z //  - int * int * int -> int //  f (1,2,3) 

眲名は、3぀のパラメヌタヌを持぀関数の眲名ずは異なるこずに泚意しおください。 タプル(int*int*int)指す矢印、パラメヌタヌ、アスタリスクは1぀だけです。


別々のパラメヌタヌで匕数を送信する必芁がある堎合、およびタプルで送信する堎合



特別な堎合.NETラむブラリのタプルず関数


.NETラむブラリを呌び出すずき、コンマは非垞に䞀般的です


これらはすべおタプルを受け入れ、呌び出しはCず同じように芋えたす。


 //  System.String.Compare("a","b") //   System.String.Compare "a" "b" 

その理由は、埓来の.NETの機胜はカリヌ化されおおらず、郚分的に適甚できないためです。 すべおのパラメヌタヌは垞にすぐに送信する必芁があり、最も明癜な方法はタプルを䜿甚するこずです。


これらの呌び出しはタプルを転送するように芋えるだけですが、これは実際には特別な堎合です。 そのような関数に実際のタプルを枡すこずはできたせん。


 let tuple = ("a","b") System.String.Compare tuple // error System.String.Compare "a","b" // error 

.NET関数を郚分的に適甚する堎合は、 以前に行ったように 、たたは以䞋に瀺すように、それらにラッパヌを曞くだけです。


 //    let strCompare xy = System.String.Compare(x,y) //    let strCompareWithB = strCompare "B" //      ["A";"B";"C"] |> List.map strCompareWithB 

個別およびグルヌプ化されたパラメヌタヌの遞択ガむド


タプルの説明は、より䞀般的なトピックに぀ながりたす。パラメヌタをい぀分離するか、グルヌプ化するかです。


この点で、FずCの違いに泚意する必芁がありたす。 Cでは、 すべおのパラメヌタヌが垞に枡されるため、この質問はそこでも発生したせん Fでは、郚分的に適甚されるため、パラメヌタヌの䞀郚のみを衚すこずができるため、パラメヌタヌを組み合わせる必芁がある堎合ず、独立しおいる堎合を区別する必芁がありたす。


独自の関数を蚭蚈する際のパラメヌタヌの構成方法に関する䞀般的な掚奚事項。



぀たり、関数を開発するずき、「このパラメヌタヌを個別に提䟛できたすか」ず自問しおください。 答えがいいえの堎合、パラメヌタヌをグルヌプ化する必芁がありたす。


いく぀かの䟋を芋おみたしょう。


 //     . //      ,       let add xy = x + y //         //      ,    let locateOnMap (xCoord,yCoord) = //  //      //      -     type CustomerName = {First:string; Last:string} let setCustomerName aCustomerName = //  let setCustomerName first last = //   //     //     //    ,     let setCustomerName myCredentials aName = // 

最埌に、パラメヌタの順序が郚分的な適甚に圹立぀こずを確認しおくださいこちらのマニュアルを参照しおください 。 たずえば、最埌の関数でmyCredentials前にaNameを配眮したのはなぜですか


パラメヌタなしの関数


パラメヌタヌを受け入れない関数が必芁になる堎合がありたす。 たずえば、耇数回呌び出すこずができる「hello world」関数が必芁です。 前のセクションで瀺したように、単玔な定矩は機胜したせん。


 let sayHello = printfn "Hello World!" //      

ただし、これは、ナニットパラメヌタヌを関数に远加するか、ラムダを䜿甚するこずで修正できたす。


 let sayHello() = printfn "Hello World!" //  let sayHello = fun () -> printfn "Hello World!" //  

その埌、関数は垞にunit匕数で呌び出される必芁がありたす。


 //  sayHello() 

.NETラむブラリずやり取りするずきに頻繁に起こるこず


 Console.ReadLine() System.Environment.GetCommandLineArgs() System.IO.Directory.GetCurrentDirectory() 

芚えおおいお、それらをunitパラメヌタで呌び出したす


新しい挔算子の定矩


1぀以䞊の挔算子文字を䜿甚しお関数を定矩できたす文字のリストに぀いおはドキュメントを参照しおください。


 //  let (.*%) xy = x + y + 1 

関数を定矩するには、文字を括匧で囲む必芁がありたす。


*始たる挔算子には、括匧ず*間にスペヌスが必芁です。 F (*はコメントの始たりずしお機胜したすCの/*...*/など


 let ( *+* ) xy = x + y + 1 

定矩埌、角括匧で囲たれおいる堎合、新しい関数は通垞の方法で䜿甚できたす。


 let result = (.*%) 2 3 

関数が2぀のパラメヌタヌで䜿甚される堎合、かっこなしで䞭眮挔算子レコヌドを䜿甚できたす。


 let result = 2 .*% 3 

!始たるプレフィックス挔算子を定矩するこずもできたす たたは~ いく぀かの制限付き、 ドキュメントを参照


 let (~%%) (s:string) = s.ToCharArray() // let result = %% "hello" 

Fでは、ステヌトメントの定矩はかなり䞀般的な操䜜であり、倚くのラむブラリは>=>や<*>ような名前のステヌトメントを゚クスポヌトしたす。


ポむントフリヌスタむル


カオスのレベルを䞋げるための最新のパラメヌタヌが欠けおいる関数の䟋は、すでに倚く芋おいたす。 このスタむルは、 ポむントフリヌスタむルたたは暗黙プログラミングず呌ばれたす 。


以䞋に䟋を瀺したす。


 let add xy = x + y //  let add x = (+) x // point free let add1Times2 x = (x + 1) * 2 //  let add1Times2 = (+) 1 >> (*) 2 // point free let sum list = List.reduce (fun sum e -> sum+e) list //  let sum = List.reduce (+) // point free 

このスタむルには長所ず短所がありたす。


利点の1぀は、䜎レベルのオブゞェクトに煩わされるのではなく、高次関数の構成に重点が眮かれおいるこずです。 たずえば、「 (+) 1 >> (*) 2 」は、明瀺的な加算ずそれに続く乗算です。 たた、「 List.reduce (+) 」は、リスト情報に関係なく、远加操䜜が重芁であるこずを明確にしたす。


無意味なスタむルを䜿甚するず、基本的なアルゎリズムに集䞭し、コヌド内の䞀般的な機胜を特定できたす。 䞊蚘で䜿甚した「 reduce 」関数は良い䟋です。 このトピックに぀いおは、リスト凊理に関する䞀連の蚈画で説明したす。


䞀方、このスタむルを䜿いすぎるず、コヌドが䞍明瞭になる可胜性がありたす。 明瀺的なパラメヌタはドキュメントずしお機胜し、その名前「リスト」などは、関数が䜕をするのかを理解しやすくしたす。


プログラミングのすべおず同様に、最良の掚奚事項は、最も明確なアプロヌチを遞択するこずです。


コンビネヌタヌ


「 コンビネヌタ 」は関数ず呌ばれ、その結果はパラメヌタのみに䟝存したす。 これは、倖の䞖界に䟝存しおいないこずを意味し、特に、他の関数やグロヌバルな倀が倖の䞖界に圱響を䞎えるこずはできたせん。


実際には、これは組み合わせ機胜がさたざたな方法でパラメヌタヌの組み合わせによっお制限されるこずを意味したす。


パむプず構成挔算子ずいういく぀かのコンビネヌタヌを芋おきたした。 それらの定矩を芋るず、さたざたな方法でパラメヌタヌを䞊べ替えおいるだけであるこずは明らかです。


 let (|>) xf = fx //  pipe let (<|) fx = fx //  pipe let (>>) fgx = g (fx) //   let (<<) gfx = g (fx) //   

䞀方、「printf」のような関数は、プリミティブですが、倖郚䞖界I / Oに䟝存しおいるため、コンビネヌタヌではありたせん。


組み合わせ鳥


コンビネヌタは、コンピュヌタおよびプログラミング蚀語よりも䜕幎も前に発明されたロゞック自然に「コンビナトリアルロゞック」ず呌ばれる党䜓の基瀎です。 組み合わせロゞックは、関数型プログラミングに非垞に倧きな圱響を及がしたす。


コンビネヌタず組み合わせロゞックの詳现に぀いおは、Raymond Smullyanの本「To Mock a Mockingbird」をお勧めしたす。 その䞭で、圌は他のコンビネヌタヌを説明し、圌らに鳥の名前を空想的に䞎えたす。 暙準コンビネヌタヌずその鳥の名前の䟋を次に瀺したす。


 let I x = x //  ,  Idiot bird let K xy = x // the Kestrel let M x = x >> x // the Mockingbird let T xy = yx // the Thrush ( !) let Q xyz = y (xz) // the Queer bird ( !) let S xyz = xz (yz) // The Starling //   ... let rec Y fx = f (Y f) x // Y-,  Sage bird 

リテラル名は非垞に暙準的なため、この甚語に粟通しおいる人なら誰でもKコンビネヌタヌを参照できたす。


倚くの䞀般的なプログラミングパタヌンは、これらの暙準的なコンビネヌタを介しお衚珟できるこずがわかりたす。 たずえば、Kestrelは、䜕かをするが元のオブゞェクトを返す、流れるようなむンタヌフェむスの䞀般的なパタヌンです。 ツグミはパむプであり、クィアは盎接的な合成であり、Yコンビネヌタヌは再垰関数を䜜成する優れた仕事をしたす。


実際、蚈算可胜な関数は、KestrelずStarlingの2぀の基本的な組み合わせのみを䜿甚しお構築できるずいう定理がありたす。


組み合わせラむブラリ


組み合わせラむブラリは、共有するように蚭蚈された倚くの組み合わせ関数を゚クスポヌトするラむブラリです。 このようなラむブラリのナヌザヌは、関数を簡単に組み合わせお、キュヌブなどのさらに倧きく耇雑な関数を簡単に取埗できたす。


適切に蚭蚈されたコンバむナラむブラリにより、高レベルの機胜に集䞭し、䜎レベルの「ノむズ」を隠すこずができたす。 「Fを䜿甚する理由」シリヌズのいく぀かの䟋で既にその力を芋おきたした。たた、 Listモゞュヌルにはそのような機胜がたくさんあり、「 fold 」ず「 map 」も考えればコンビネヌタヌです。


コンビネヌタのもう1぀の利点は、最も安党なタむプの関数であるこずです。 なぜなら それらは倖の䞖界に䟝存せず、グロヌバル環境が倉化しおも倉化するこずはできたせん。 グロヌバル倀を読み取る関数たたはラむブラリ関数を䜿甚する関数は、コンテキストが倉曎されるず、呌び出し間で䞭断たたは倉曎される堎合がありたす。 これはコンビネヌタヌには決しお起こりたせん。


Fでは、コンビネヌタラむブラリを解析FParsec、HTMLの䜜成、フレヌムワヌクのテストなどに䜿甚できたす。 コンビネヌタに぀いおは、次のシリヌズの埌半で説明し、䜿甚したす。


再垰関数


倚くの堎合、関数はその本䜓から自身を参照する必芁がありたす。 兞型的な䟋は、フィボナッチ関数です。


 let fib i = match i with | 1 -> 1 | 2 -> 1 | n -> fib(n-1) + fib(n-2) 

残念ながら、この関数はコンパむルできたせん。


 error FS0039: The value or constructor 'fib' is not defined 

recキヌワヌドを䜿甚しお、これが再垰関数であるこずをコンパむラヌに䌝える必芁がありたす。


 let rec fib i = match i with | 1 -> 1 | 2 -> 1 | n -> fib(n-1) + fib(n-2) 

関数型プログラミングでは再垰的な関数ずデヌタ構造が非垞に䞀般的であり、このトピックに぀いおは埌でシリヌズ党䜓を取り䞊げたいず思いたす。


远加のリ゜ヌス


Fには、CたたはJavaの経隓がある人向けの資料など、倚くのチュヌトリアルがありたす。 次のリンクは、Fの詳现を説明するのに圹立ちたす。



Fの孊習を開始する他のいく぀かの方法に぀いおも説明したす。


最埌に、Fコミュニティは非垞に初心者に優しいです。 Slackには、FSoftware Foundationがサポヌトする非垞に掻発なチャットがあり、 自由に参加できる初心者ルヌムがありたす。 これを行うこずを匷くお勧めしたす


ロシア語を話すコミュニティFのサむトを蚪れるこずを忘れないでください 蚀語の孊習に぀いお質問がある堎合は、チャットルヌムでお気軜にご盞談ください。



翻蚳著者に぀いお


@kleidemosによる翻蚳
翻蚳ず線集䞊の倉曎は、F開発者のロシア語コミュニティの努力によっお行われたした。 たた、この蚘事を公開する準備をしおくれた@schvepsssず@shwarsにも感謝したす。



Source: https://habr.com/ru/post/J433398/


All Articles