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
依存プロパティ(依存関係プロパティ)でバインディング - 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 だけ切り替えるとできるかな?