Scalaが読めなくて辛い。
タイトルの通り、全然読めなくて、一日を無駄にしそうなので、ブログを書く。
前提
呼び元はこんな感じ。
@myCheckBoxes(field = formValue("testVal"), options(models.TestVal.getList), 'name -> "testVal.value")
formValue("testVal")
のformValue
は、PlayFrameworkのForm.class
です。
んで、options
は、models.TestVal
クラスから、Map
を取得しています。
複数の値をチェックボックス化するヘルパーを以前つくったんだけど、編集画面で登録済み、つまりchecked
をいれてほしいんだけど、それの原因がわからないので、今回のソースリーディングとなる。
読みとくぞ―!
ちなみに初期のcheckboxのテンプレートはこんな感じ
@** * Generate an HTML checkbox group * * Example: * {{{ * @inputCheckboxGroup( * contactForm("hobbies"), * options = Seq("S" -> "Surfing", "R" -> "Running", "B" -> "Biking","P" -> "Paddling"), * '_label -> "Hobbies", * '_error -> contactForm("hobbies").error.map(_.withMessage("select one or more hobbies"))) * * }}} * * @param field The form field. * @param options Sequence of options as pairs of value and HTML * @param args Set of extra HTML attributes. * @param handler The field constructor. *@ @(field: play.api.data.Field, options: Seq[(String,String)], args: (Symbol,Any)*)(implicit handler: FieldConstructor, messages: play.api.i18n.Messages) @input(field, args.map{ x => if(x._1 == '_label) '_name -> x._2 else x }:_*) { (id, name, value, htmlArgs) => <span class="buttonset" id="@id"> @defining(field.indexes.map( i => field("[%s]".format(i)).value ).flatten.toSet) { values => @options.map { v => <input type="checkbox" id="@(id)_@v._1" name="@{name + "[]"}" value="@v._1" @if(values.contains(v._1)){checked="checked"} @toHtmlArgs(htmlArgs)/> <label for="@(id)_@v._1">@v._2</label> } } </span> }
んで、改良。。。というよりは必要な部分だけ残しました。
@(field: play.api.data.Field, options: Seq[(String,String)], args: (Symbol,Any)*)(implicit handler: FieldConstructor) @input(field, args.map{ x => if(x._1 == '_label) '_name -> x._2 else x }:_*) { (id, name, value, htmlArgs) => @defining(field.indexes.map( i => field("[%s]".format(i)).value ).flatten.toSet) { values => @options.map { v => <input type="hidden" id="@(id)_@v._1" name="@{name + "[]"}" @if(values.contains(v._1)){value="@v._1"} @toHtmlArgs(htmlArgs)/> } } }
これからimplicitとか触らないといけないんだけど、とりあえず、これでいいと思ったんだ。
だがしかし。
Scalaがよくわからなくて中々前にすすまないお(^ω^ = ^ω^)おっおっおっ!
ログ出して見てみるけど、本当に意味がわからないんだ!特に@xxx()
の後の、xxx =>
これ!!なんなの?(´・ω・`)
とりあえずチュートリアルを読もうと思いました。
よんできたお(^ω^
どうやら無名関数で使う演算子のもよう
scala> val fun = (x:Int, y:Int) => x + y fun: (Int, Int) => Int = <function2> scala> fun(1,2) res4: Int = 3
っつーことは最初のプログラムは
@input(field, args.map{ x => if(x._1 == '_label) '_name -> x._2 else x }:_*) { (id, name, value, htmlArgs) => @defining() }
んで@defining()
には下記値が。
@defining(field.indexes.map( i => field("[%s]".format(i)).value ).flatten.toSet) { values => @options.map }
そして、@options.map
には。
@options.map { v => "<intput ... >"}
ってな別け方でいいのかな?
@input?
playframeworkで用意されているテンプレートですね。ここを確認したらわかります。
@** * Prepare a generic HTML input. *@ @(field: play.api.data.Field, args: (Symbol, Any)* )(inputDef: (String, String, Option[String], Map[Symbol,Any]) => Html)(implicit handler: FieldConstructor, messages: play.api.i18n.Messages) @id = @{ args.toMap.get('id).map(_.toString).getOrElse(field.id) } @handler( FieldElements( id, field, inputDef(id, field.name, field.value, args.filter(arg => !arg._1.name.startsWith("_") && arg._1 != 'id).toMap), args.toMap, messages ) )
ぶっちゃけ、このソースは何を意味しているのか全然わかりません。(°ω°
なので、シカトでいきます。
@defining?
こういう意味らしい
@defining(変数にする値の算出式) { 変数名 => 【出力内容】 }
ということは、field.indexes.map( i => field("[%s]".format(i)).value ).flatten.toSet
が算出式で、values
が変数名ですか。
これ => field.indexes.map( i => field("[%s]".format(i)).value ).flatten.toSet
も読み解いていきます。
field.indexes.map() ?
PlayFrameworkのドキュメントを見ると、受け取ったfield
の値からindex番号をList
で取得するっぽい
Return the indexes available for this field (for repeated fields ad List)
そこで続けてmap
というわけですか。(いまいちよくわかっていない)
まずは試してみましょう。
scala> val numbers = List(1, 2, 3) numbers: List[Int] = List(1, 2, 3) scala> numbers.map( i => i ) res7: List[Int] = List(1, 2, 3)
(´・ω・)?
どゆこと?
なんか、元のソースがi
だったからそれにしたんだけど、a
だったらどうなるの?
scala> numbers.map( a => 3 ) res12: List[Int] = List(3, 3, 3)
どゆこと?(´°ω°`)?
謎が深まってまいりました 。プログラマを名乗って申し訳ございません。
どうして値がかわったの? そもそもList
にmap
っておかしくね?
まだまだ初心者なので、map
のリファレンスを探しにいく。
・・・・どうやら私はものすごく勘違いをしているんじゃないか。。。。?
まだ目的はつかめていないが、どうやらmap
関数は引数に関数を渡すらしい。
なんかすごくわかりにくいけど、こういうことだ。
scala> val fun = (x:Int, y:Int) => x + y fun: (Int, Int) => Int = <function2> scala> val numbers = List(1, 2, 3) numbers: List[Int] = List(1, 2, 3) scala> numbers.map(x => fun(x, x)) res19: List[Int] = List(2, 4, 6)
なるほど。map(x => fun())
はx
自体は引数で、=>
の右はその変数が使える式ってことか。
わからなすぎてゾッとする。俺コレまでどうやって組んできたんだろう。。。?(°ω°;
ということは、jQueryとかでの$.each(array, function(i, ix) { / 式 / } )
のような動き方をしているんだな。ただ、返り値がリストになるってことか。
うんうん。それなら、さっきの動作な納得がいく。
scala> numbers.map( a => 3 ) res12: List[Int] = List(3, 3, 3)
ただループしているだけだから、当たり前さね。
戻りますが、となるとfield.indexes.map( i => field("[%s]".format(i)).value ).flatten.toSet
はなんとなく動作イメージがつきました。field("[%s]".format(i)).value
の結果がList
で帰ってくるはず。
.flatten?
flatten
については、「要素がTraversableのとき、それを外して平坦化する。」らしい。。。。?(°∀° 意味不明でワロタ
よくわかりませんが、とりあえずList
で返る値にこの関数を実行してみましょう。
scala> val numbers = List(1, 2, 3) numbers: List[Int] = List(1, 2, 3) scala> numbers.map(i => i + 1).flatten <console>:9: error: No implicit view available from Int => scala.collection.GenTraversableOnce[B]. numbers.map(i => i + 1).flatten
ワロタ。もうわからん。。。(´;ω;`)ブワッ
・・・・調べたんだけど、どゆことかまだわかっていない
scala> val numbers = List(List(1,2), List(2,3), List(3,5)) numbers: List[List[Int]] = List(List(1, 2), List(2, 3), List(3, 5)) scala> numbers.map(i => i).flatten res32: List[Int] = List(1, 2, 2, 3, 3, 5)
まだよくわかっていないんだけど、、、List
の中にList
。つまり、多次元二次元の場合、flatten
を使うと、それを、多次元二次元 => 1次元に変換してくれるらしい。。。
ちなみに多次元と書いたけど、二次元しか試していない。
scala> val numbers = List(List(1,2), List(2,3), List(3,List(5,6),5)) numbers: List[List[Any]] = List(List(1, 2), List(2, 3), List(3, List(5, 6), 5)) scala> numbers.map(i => i).flatten res35: List[Any] = List(1, 2, 2, 3, 3, List(5, 6), 5)
というわけで、二次元のみに修正します。
さて、次はこいつ
toSet ?
こいつは、「Setに変換する。」らしいです。わからないこと多すぎてワロタ(°∀°
Set
ってなんぞや。調べます。
重複をゆるさないコレクション型らしい。
scala> val numbers = List(List(1,2), List(2,3), List(3,5)) numbers: List[List[Int]] = List(List(1, 2), List(2, 3), List(3, 5)) scala> numbers.map(i => i).flatten.toSet res39: scala.collection.immutable.Set[Int] = Set(1, 2, 3, 5)
まじでした。Set
しゅごい。最近Javaでもやったけど、、、なんだっけあの型・・・?TreeMap
これだな。
field.indexes.map( i => field("[%s]".format(i)).value ).flatten.toSet
まとめると、field
から、indexes
でkeyをList
で取得して、map
関数で、ループして、中のfield("[%s]".format(i)).value
は。。。多分keyからvalueを取得しているのでしょう。んでそれをList
。そして、二次元になっていたら、一次元にして、最後にtoSet
でSet型にすると。。。
長い!( ゚∀゚)・∵. グハッ!!
まとめ
っつーことで、@defining
の結果が、values
に渡りました。あー、なんとなくだけど読めるようになりました。
=>
の意味がわかっただけどもよかった。。。( ´ー`)フゥー...
まだ完璧に読めたわけじゃないので、引き続き頑張る。