golangチャレンジ:構造体x
を受け取り、内部にあるすべての文字列フィールドに対してfn
を呼び出す関数walk(x interface{}, fn func(string))
を記述します。難易度:再帰的に。
コンピューティングにおけるリフレクションは、プログラムが、特にタイプを通じて、独自の構造を調べる能力です。それは一種のメタプログラミングです。また、混乱の元にもなります。
interface
)」とは何ですか?string
、int
などの既知の型や、BankAccount
などの独自の型で機能する関数の点で、タイプの安全性を提供してきました。interface{}
で回避できます。walk(x interface{}, fn func(string))
は、x
の任意の値を受け入れます。interface
」をとる関数のユーザーとして、タイプの安全性を失います。タイプstring
のFoo.bar
を関数に渡すつもりでしたが、代わりにint
であるFoo.baz
を渡した場合はどうなりますか?コンパイラーは間違いを通知できません。また、関数に渡すことが許可されている what もわかりません。たとえば関数が UserService
をとることを知ることは非常に便利です。interface
ではなく混乱を防ぐために)の周囲で設計できるかどうか検討してください。x
)に含まれている構造体で関数を呼び出す必要があります。 次に、渡された関数(fn
)をスパイして、呼び出されているかどうかを確認できます。walk
によってfn
に渡されたかを格納する文字列のスライス(got
)を格納したいと思います。多くの場合、前の章では、関数/メソッドの呼び出しをスパイするために専用の型を作成しましたが、この場合は、got
を閉じるfn
の匿名関数を渡すだけです。Name
フィールドを持つ匿名のstruct
を使用します。walk
をx
とスパイで呼び出し、今のところgot
の長さを確認するだけです。非常に基本的な動作が得られたら、アサーションでより具体的になります。walk
を定義する必要がありますfn
の呼び出し対象をより具体的にアサートすることです。fn
に渡された文字列が正しいことを確認しますx
を確認し、そのプロパティを確認する必要があります。String()
を呼び出しますが、フィールドが文字列以外の場合は間違っていることがわかります。fn
が呼び出された文字列の配列をチェックするいくつかのテストを作成します。cases
に追加します。val
には、値のフィールド数を返すメソッドNumField
があります。 これにより、フィールドを反復処理し、テストに合格したfn
を呼び出すことができます。walk
の次の欠点は、すべてのフィールドがstring
であると想定していることです。 このシナリオのテストを書いてみましょう。string
であることを確認する必要があります。struct
があるとどうなりますか?struct
の構造を推測できるはずです。Kind
をもう一度調べ、それが「構造体struct
」である場合は、その内部の「構造体struct
」でもう一度walk
を呼び出すだけです。switch
にリファクタリングすると、読みやすさが向上し、コードの拡張が容易になります。Value
でNumField
を使用することはできません。Elem()
を使用する前に、基になる値を抽出する必要があります。interface{}
から reflect.Value
を関数に抽出する責任をカプセル化しましょう。x
のreflect.Value
を取得します。方法は気にしません。reflect.Value
で NumField
を呼び出そうとしていますが、構造体ではないため、これはありません。walk
と呼びたいreturn
付き)であるかどうかを確認し、そうでない場合は構造体であると想定します。walk
を呼び出してその値を繰り返し処理します。 それ以外の場合、reflect.String
であれば、fn
を呼び出すことができます。walk
を呼び出すという操作の繰り返しがありますが、概念的には同じです。value
がreflect.String
の場合、通常のようにfn
を呼び出すだけです。switch
はタイプに応じて2つのものを抽出しますValue
を抽出する方法(Field
またはIndex
)getField
関数の結果を使用して、numberOfValues
が walk
を呼び出して反復できるようにします。map
です。map
はstruct
に非常に似ていることがわかります。これは、コンパイル時にキーが不明であるということだけです。walk
を導入しました。これは、val
からreflect.Value
を抽出するだけでよいように、switch
内のwalk
への呼び出しを乾燥させます。fn
の呼び出しは特定の順序で行われると断言するため、テストが失敗することがあります。assertContains
の定義方法は次のとおりですchan
です。Recv()
で閉じられるまで、チャネルを通じて送信されたすべての値を反復処理できますfunc
です。reflect
パッケージのいくつかの概念を導入しました。