オブジェクト指向のGoが繰り返し、感情的に議論される方法。 次に、それがどれほど機能的かを評価してみましょう。 コンパイラは末尾再帰の最適化を行わないことにすぐに注意してください。 なんで? 「ループのある言語ではこれは必要ありません。 プログラマーが再帰コードを書くとき、彼は呼び出しスタックを表現したい、またはループを書きたいのです。 しかし、この言語には本格的なラムダ、クロージャー、再帰型、および多くの機能があります。 機能的な方法でそれらを適用してみましょう。 最初の例はサンドボックスですぐに実行可能に記述され、2番目の例では手続き言語ですべて記述されているため、例は合成のように見えます。 Goプログラミングと関数型プログラミングの両方に精通することになっています。説明はほとんどありませんが、コードはコメントされています。
クロージャー、クロージャーは、完全に言語で実装されています。
たとえば、次のように遅延再帰シーケンスを取得できます
func produce(source int, permutation func(int) int) func() int { return func() int {
単純な擬似乱数バリエーター
func mutate(j int) int { return (1664525*j + 1013904223) % 2147483647 }
そして、ここに乱数ジェネレーターがあります
next := produce(1, mutate) next()
実施例 package main import ( "fmt" ) func produce(source int, permutation func(int) int) func() int { return func() int {
サンドボックスで試すカレー。 カリー化、引数の1つに関数を適用することは一般に実装されていません。 ただし、プライベートタスクは解決されます。 たとえば、標準ライブラリ時間の遅延呼び出し関数にはシグネチャ
func AfterFunc(d Duration、f func())があります
* Timerは引数
func()を受け取り、さらにパラメータ化された
func(arg MyType)を渡します。 そして、私たちはそのようにすることができます
type MyType string
Goのメソッドは、受益者の最初の引数を取る関数です
式
MyType.JustPrintは、この関数に署名
func(arg MyType)を与えます。これは、引数
MyType.JustPrint(“ Eat me”)に適用できます
それどころか、式
arg.JustPrintは、目覚まし時計に渡すことができる署名
func()を持つ
argに適用される
JustPrint関数を提供します
timer := time.AfterFunc(50 * time.Millisecond, arg.JustPrint)
実施例 package main import ( "fmt" "time" ) type MyType string
サンドボックスで試してください。一流の施設としての継続である継続は、スネムの優雅さでは実現しません。 一方、組み込み関数
panic() long計算を中断し、呼び出し元の場所に結果(エラーなど)を返すことができるlong_jumpのおおよその類似物があります。 例外の処理に加えて
、panic()、defer recover()の構築を使用して、たとえば
、深すぎた再帰
をパススルーすることができます(これは
encoding.jsonパッケージで確認されています)。 この意味で、デザインは一流であり、排他的ではありません。 不要な再帰から抜け出す方法は、強調する価値があり、継続の古典的なアプリケーションです。
これは単純な、最適化されていない(プロダクションでは使用されない!!)再帰関数で、n番目のフィボナッチ数を前の
func Fib(n int) int { if n == 0 { return 0 } if n == 1 { return 1 } first := Fib(n - 1) second := Fib(n - 2) if first > max {
したがって、最大値より大きくない限り、n番目のフィボナッチ数を取得したい継続(call / cc)で呼び出します。
var max int = 200 func CallFib(n int) (res int) { defer func() { if r := recover(); r != nil {
実用的な例。 package main import "fmt" var max int = 1000 func Fib(n int) int { if n == 0 { return 0 } if n == 1 { return 1 } first := Fib(n - 1) second := Fib(n - 2) if first > max {
サンドボックスで試してください。Haskellは手続き言語のモナドを必要としません。 一方、Goでは、再帰的な型宣言が完全に許可されており、多くの人がモナドを構造的再帰の一種と見なしています。 ロブパイクは、ステートマシン、ステートマシンの次の定義を提案しました。
type stateFn func(Machine) stateFn
ここで、状態は、アクションを生成して新しい状態を返すマシンの機能です。
このようなマシンの操作は簡単です。
func run(m Machine) { for state := start; state != nil; { state = state(m) } }
Haskell State Monadに似ていません。
最小限のパーサーを作成します。他に何をするために、着信ストリームから数値を選択するステートマシンが必要です。
type stateFn func(*lexer) stateFn type lexer struct { *bufio.Reader
必要な状態は2つだけです
func lexText(l *lexer) stateFn { for r, _, err := l.ReadRune(); err != io.EOF; r, _, err = l.ReadRune() { if '0' <= r && r <= '9' {
実用的な例。 package main import ( "bufio" "fmt" "io" "strconv" "strings" ) type stateFn func(*lexer) stateFn func run(l *lexer) { for state := lexText; state != nil; { state = state(l) } } type lexer struct { *bufio.Reader
サンドボックスで試してください。リアクティブプログラミングを正式に記述することは困難です。 これは、ストリームと信号に関するものです。 Goには両方があります。 標準のioライブラリは
io.Readerおよび
io.Writerインターフェイスを提供し
ます。これらの
インターフェイスにはそれぞれ
Read()および
Write()メソッドがあり、ストリームの概念を調和的に反映しています。 たとえば、ファイルとネットワーク接続は両方のインターフェイスを実装します。 インターフェイスは、データソースに関係なく使用できます。
Decoder = NewDecoder(r io.Reader) err = Decoder.Decode(Message)
ファイルやネットワーク接続などを均一にエンコードします。
信号の概念は、言語の構文に組み込まれています。
chan (チャネル)タイプには<-メッセージ転送演算子が装備されており、一意の
select {case <-chan}構造により、複数のチャネルから送信可能なチャネルを選択できます。
非常に単純なストリームミキサーを作成しましょう。
入力として、文字列を使用します(サンドボックスでサンプルをすぐに実行できるようにすることに同意しました。これにより、選択が制限されます。ネットワーク接続からの読み取りはさらに興味深いでしょう。
reader1 := strings.NewReader(" ") reader2 := strings.NewReader(" ")
出力は標準出力ストリームを受け入れます。
writer := os.Stdout
タイマーチャネルを制御信号として使用します。
stop := time.After(10000 * time.Millisecond) tick := time.Tick(150 * time.Millisecond) tack := time.Tick(200 * time.Millisecond)
ミキサー全体
select { case <-tick: io.CopyN(writer, reader1, 5) case <-tack: io.CopyN(writer, reader2, 5) case <-stop: return }
実用的な例。 package main import ( "io" "os" "strings" "time" ) func main() { stop := time.After(10000 * time.Millisecond) tick := time.Tick(150 * time.Millisecond) tack := time.Tick(200 * time.Millisecond) reader1 := strings.NewReader(" ") reader2 := strings.NewReader(" ") writer := os.Stdout for { select { case <-tick: io.CopyN(writer, reader1, 5) case <-tack: io.CopyN(writer, reader2, 5) case <-stop: return } } }
サンドボックスで試してください。