moonmile solutions 分室

ソフトウェア開発者の情報収集をリアルタイムで垂れ流し...という具合に行きます。

F#でラムダ式を配列に入れる離れ業がある

pars.fs を解析しているときに気づいたのだが、

let funcs = [|
    (fun x -> x + 2);
    (fun x -> x * 2);
    (fun x -> x / 2)
    |]

ってことができる。ラムダ式を配列にするのだけど、こういうことができる。

> funcs.[1] 10 ;;
val it : int = 20

あらかじめ、引数が1つのint型のラムダ式を入れる、ってのを推測してくれるから、

let funcs = [|
    (fun x -> x + 2);
    (fun x -> x * 2);
    (fun x -> x / 2);
    (fun x y -> x + y )
    |]

は、fun x y のところでコンパイルエラーになる。

これって C# で書くとどうなるんだろうか?

本格的に F# の二項演算子(中置記法)を導入してみる(3)

ちなみに、pars.fs 自体は、以下のように定義されている。

declExpr:
  | defnBindings IN typedSeqExpr  %prec expr_let 
     { mkLocalBindings (unionRanges (rhs2 parseState 1 2) $3.Range,$1,$3) }
...
  | declExpr  JOIN_IN               declExpr { SynExpr.JoinIn($1,rhs parseState 2,$3,unionRanges $1.Range $3.Range) }
  | declExpr  BAR_BAR               declExpr { mkSynInfix (rhs parseState 2) $1 "||" $3 }
  | declExpr  INFIX_BAR_OP          declExpr { mkSynInfix (rhs parseState 2) $1 $2 $3 }
  | declExpr  OR                    declExpr { mkSynInfix (rhs parseState 2) $1 "or" $3 }
  | declExpr  AMP                   declExpr { mkSynInfix (rhs parseState 2) $1 "&" $3 }
  | declExpr  AMP_AMP               declExpr { mkSynInfix (rhs parseState 2) $1 "&&" $3 }
  | declExpr  INFIX_AMP_OP          declExpr { mkSynInfix (rhs parseState 2) $1 $2 $3 }
  | declExpr  EQUALS                declExpr { mkSynInfix (rhs parseState 2) $1 "=" $3 }
  | declExpr  INFIX_COMPARE_OP      declExpr { mkSynInfix (rhs parseState 2) $1 $2 $3 }
  | declExpr  DOLLAR                declExpr { mkSynInfix (rhs parseState 2) $1 "$" $3 }
  | declExpr  LESS                  declExpr { mkSynInfix (rhs parseState 2) $1 "<" $3 }
  | declExpr  GREATER               declExpr { mkSynInfix (rhs parseState 2) $1 ">" $3 }
  | declExpr  INFIX_AT_HAT_OP       declExpr { mkSynInfix (rhs parseState 2) $1 $2 $3 }
  | declExpr  PERCENT_OP            declExpr { mkSynInfix (rhs parseState 2) $1 $2 $3 }
  | declExpr  COLON_COLON           declExpr { SynExpr.App (ExprAtomicFlag.NonAtomic, true, mkSynIdGet (rhs parseState 2) opNameCons,SynExpr.Tuple ([$1;$3],[rhs parseState 2],unionRanges $1.Range $3.Range),unionRanges $1.Range $3.Range) }
  | declExpr  PLUS_MINUS_OP         declExpr { 
    printfn "op +"
    mkSynInfix (rhs parseState 2) $1 $2 $3 }
  | declExpr  MINUS                 declExpr { 
    mkSynInfix (rhs parseState 2) $1 "-" $3 }

operator 自体がべたべたで書いてあるのは、lex.fsl でやたらに定義してあるからなんだけど、ここに

 | declExpr ident declExpr

してもうまくいかない。多分 local bind しているところの nameop を使うのかも。

本格的に F# の二項演算子(中置記法)を導入してみる(2)

D:\git\fsharp\src\utils\prim-parsing.fs の 115行目あたりに

 

#if DEBUG

module Flags = 

    let mutable debug = true

#endif

 

があるので debug = true にしておくと、pars.fs の解析途中が見られる。

 

 0 value(state), state 4:op x y ;;

keyword:"op"

keyword:"x"

reduce popping 0 values/states, lookahead IDENT "op" goto state 33

1 value(state), state 4-33:reduce popping 0 values/states, lookahead IDENT "op" goto state 34

2 value(state), state 4-33-34:shift/consume input IDENT "op", shift to state 1642

3 value(state), state 4-33-34-1642:reduce popping 1 values/states, lookahead [TBC] IDENT "op" goto state 1691

3 value(state), state 4-33-34-1691:reduce popping 1 values/states, lookahead [TBC] IDENT "op" goto state 1698

3 value(state), state 4-33-34-1698:reduce popping 1 values/states, lookahead [TBC] IDENT "op" goto state 1236

3 value(state), state 4-33-34-1236:reduce popping 1 values/states, lookahead [TBC] IDENT "op" goto state 1211

3 value(state), state 4-33-34-1211:keyword:"y"

reduce popping 1 values/states, lookahead IDENT "x" goto state 1208

3 value(state), state 4-33-34-1208:shift/consume input IDENT "x", shift to state 1642

4 value(state), state 4-33-34-1208-1642:reduce popping 1 values/states, lookahead [TBC] IDENT "x" goto state 1691

4 value(state), state 4-33-34-1208-1691:reduce popping 1 values/states, lookahead [TBC] IDENT "x" goto state 1698

4 value(state), state 4-33-34-1208-1698:reduce popping 1 values/states, lookahead [TBC] IDENT "x" goto state 1236

4 value(state), state 4-33-34-1208-1236:reduce popping 1 values/states, lookahead [TBC] IDENT "x" goto state 1214

4 value(state), state 4-33-34-1208-1214:reduce popping 1 values/states, lookahead IDENT "y" goto state 1210

4 value(state), state 4-33-34-1208-1210:reduce popping 2 values/states, lookahead IDENT "y" goto state 1208

3 value(state), state 4-33-34-1208:shift/consume input IDENT "y", shift to state 1642

4 value(state), state 4-33-34-1208-1642:reduce popping 1 values/states, lookahead [TBC] IDENT "y" goto state 1691

4 value(state), state 4-33-34-1208-1691:reduce popping 1 values/states, lookahead [TBC] IDENT "y" goto state 1698

4 value(state), state 4-33-34-1208-1698:reduce popping 1 values/states, lookahead [TBC] IDENT "y" goto state 1236

4 value(state), state 4-33-34-1208-1236:reduce popping 1 values/states, lookahead [TBC] IDENT "y" goto state 1214

4 value(state), state 4-33-34-1208-1214:reduce popping 1 values/states, lookahead SEMICOLON_SEMICOLON goto state 1210

4 value(state), state 4-33-34-1208-1210:reduce popping 2 values/states, lookahead SEMICOLON_SEMICOLON goto state 1208

3 value(state), state 4-33-34-1208:reduce popping 1 values/states, lookahead SEMICOLON_SEMICOLON goto state 1085

3 value(state), state 4-33-34-1085:reduce popping 1 values/states, lookahead SEMICOLON_SEMICOLON goto state 35

3 value(state), state 4-33-34-35:reduce popping 3 values/states, lookahead SEMICOLON_SEMICOLON goto state 21

1 value(state), state 4-21:shift/consume input SEMICOLON_SEMICOLON, shift to state 16

2 value(state), state 4-21-16:reduce popping 1 values/states, lookahead [TBC] SEMICOLON_SEMICOLON goto state 22

2 value(state), state 4-21-22:reduce popping 2 values/states, lookahead [TBC] SEMICOLON_SEMICOLON goto state 6

1 value(state), state 4-6:reduce popping 1 values/states, lookahead [TBC] SEMICOLON_SEMICOLON goto state 5

1 value(state), state 4-5:val it : int = 30

>

Parser: interpret tables

0 value(state), state 4:

 ここの state 4-33-34-1208 が yacc の解析スタックなのだが、この番号は  fyacc が生成する _fsyacc_reductions に対応している。コンパイル時に番号が決まるので、元の pars.fsy に書かれている構文解析と対応がこのままではできない。

localBindings: 

  | attr_localBinding moreLocalBindings 

      { let (moreBindings, moreBindingRanges) = List.unzip $2

        let moreLocalBindingsLastRange = if moreBindingRanges.IsEmpty then None else Some (List.last moreBindingRanges)

        match $1 with 

        | Some (localBindingRange,attrLocalBindingBuilder) -> 

           let lastRange = 

               match moreLocalBindingsLastRange with 

               | None -> localBindingRange 

               | Some m -> m

           Some lastRange, (fun attrs vis mLetKwd -> attrLocalBindingBuilder attrs vis mLetKwd true ::  moreBindings)

        | None -> 

           moreLocalBindingsLastRange, (fun _attrs _vis _letm -> moreBindings) }

 

 これを変換した後に、以下のようなラムダ式になることは分かったので、state no(配列の番号)と "localBindings" の文字列を突き合わせる手段があれば、解析が進む。

        (fun (parseState : Internal.Utilities.Text.Parsing.IParseState) ->

            let _1 = (let data = parseState.GetInput(1) in (Microsoft.FSharp.Core.Operators.unbox data : 'attr_localBinding)) in

            let _2 = (let data = parseState.GetInput(2) in (Microsoft.FSharp.Core.Operators.unbox data : 'moreLocalBindings)) in

            Microsoft.FSharp.Core.Operators.box

                (

                   (

# 2189 "..\pars.fsy"

                             let (moreBindings, moreBindingRanges) = List.unzip _2

                             let moreLocalBindingsLastRange = if moreBindingRanges.IsEmpty then None else Some (List.last moreBindingRanges)

                             match _1 with 

                             | Some (localBindingRange,attrLocalBindingBuilder) -> 

                                let lastRange = 

                                    match moreLocalBindingsLastRange with 

                                    | None -> localBindingRange 

                                    | Some m -> m

                                Some lastRange, (fun attrs vis mLetKwd -> attrLocalBindingBuilder attrs vis mLetKwd true ::  moreBindings)

                             | None -> 

                                moreLocalBindingsLastRange, (fun _attrs _vis _letm -> moreBindings) 

                   )

# 2189 "..\pars.fsy"

                 : 'localBindings));

 

 _fsyacc_reductions の実体は、tables.reductions になる。ここの actionValue action が int 型を返して、reductions 配列から拾ってくる。

                ) elif kind = reduceFlag then

                    let prod = actionValue action                                     

                    let reduction = reductions.[prod]                                                             

                    let n = int tables.reductionSymbolCounts.[prod]

                       // pop the symbols, populate the values and populate the locations                              

#if DEBUG

                    if Flags.debug then Console.Write("reduce popping {0} values/states, lookahead {1}", n, report haveLookahead lookaheadToken);

#endif

 

 

 

 

本格的に F# の二項演算子(中置記法)を導入してみる(1)

Scala のメソッドの扱いが、func x y と x func y が同じなのは、二項演算子に文字列を使えるのではなくて「中置記法(infix notation)」とのこと。C# や F# でも (+) を op_plus に置き換えているのだが、一方向にしかできない。Scala の場合は、双方向に変換できるので等価性がある、ってことだろう。

現状の F# では、

let (+++) x y = x + y

のときに

x +++ y は可能

(+++)  x y はできない。

let plus x y = x + y

のときに

plus x y は可能

 x plus y はできない

となっている。これを両方可能にすれば ok. そうすると関数/メソッドも中置記法になるので「見かけ上、二項演算子のように見える」ことになる。

実際には Scala の infix notation は obj.method param の書き方を obj method param で書けるということなので、

member this.op x = this + x の時は

obj.op x と書くところを

obj op x と書けることになる。

よって、先の global な +++ と区別が必要なので、

member this.op x  = ... の場合には

obj op x と書く

static member this.op x y = ... の場合は(こんな書き方ができたかは後で)

x class.op y と書く

グローバルなところで

let op x y = ... と書いたら

x op y と書ける

の実装が必要になる。

 

これが他の F# の文法を阻害しないかどうかはもう少し検討が必要。

 

 

INotifyPropertyChangedとDependencyProperties

WPFで依存プロパティを作るときに DependencyProperties を使って、ViewModel のときに INotifyPropertyChanged を使っていたのだが、どちらも同じ機能だから、どっちかを使えばよいらしい。INotifyPropertyChanged はリフレクションを使っているので若干遅くて、DependencyProperties は XAML 依存。

 

Xamarin.iOS/Android と共有したい場合は、INotifyPropertyChanged を使わざるを得ないので、こうなると UI の(storyboad/axml)も INotifyPropertyChanged を使えばよいのかな?

 

基礎 : 依存関係プロパティと通知

http://msdn.microsoft.com/ja-jp/magazine/cc794276.aspx

DependencyProperties or INotifyPropertyChanged ? - CodeProject

http://www.codeproject.com/Articles/62158/DependencyProperties-or-INotifyPropertyChanged

wpf - INotifyPropertyChanged vs. DependencyProperty in ViewModel - Stack Overflow

http://stackoverflow.com/questions/291518/inotifypropertychanged-vs-dependencyproperty-in-viewmodel

binding - When to use a WPF Dependency Property versus INotifyPropertyChanged - Stack Overflow

http://stackoverflow.com/questions/3551204/when-to-use-a-wpf-dependency-property-versus-inotifypropertychanged

 

依存プロパティ(依存関係プロパティ)でバインディング - cu39's diary

http://cu39.hateblo.jp/entry/20090723/1248342532

Nine Works: WPFコントロールに独自のプロパティを作成する

http://nine-works.blog.ocn.ne.jp/blog/2011/06/wpf_0621.html

XAML の高度な機能(WPF) (.NET Framework)

http://ufcpp.net/study/dotnet/wpf_xamladv.html#dependency

 

歴史的に WPF/Silverlight が広まった後に MVVM が来ているので、XAML の範疇に特化したものと、MvvmCross などのクロスプラットフォーム MVVM と混在してきている。

 

Visual Studio 2013 のプロジェクト GUID

HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\12.0_Config\Projects

に一覧がある。

 

[HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\12.0_Config\Projects\{6bc8ed88-2882-458c-8e55-dfd12b67127b}]
@="MonoTouchProjectFactory"
"DisplayName"="Xamarin.iOS"
"DisplayProjectFileExtensions"="Xamarin.iOS Projects (*.csproj);*.csproj"
"Package"="{77875fa9-01e7-4fea-8e77-dfe942355ca1}"
"ProjectTemplatesDir"="\\..\\NullPath"
"Language(VsTemplate)"="CSharp"
"ShowOnlySpecifiedTemplates(VsTemplate)"=dword:00000001
"TemplateGroupIDs(VsTemplate)"="MonoTouch"
"TemplateIDs(VsTemplate)"="Microsoft.CSharp.XmlFile,Microsoft.CSharp.TextFile,Microsoft.CSharp.CodeFile,Microsoft.CSharp.Class,Microsoft.CSharp.Resource,Microsoft.CSharp.AssemblyInfo.Internal"

こんな感じ。

これの VisualBasic 版を作ればいいと思う。

 

 

オレオレF#のインテリセンス機能

記号を使えるとしても、VS2013 で使えないと意味がないので、ファイル編集のインテリセンスと、F# Interactive をオレオレF#に切り替える必要がある...が、どうするのか?

FSharp.Comiler.dll だけ切り替えるとできるかな?