F# の例(1)
F# で動作するようにすることはできましたが、まだよくわからないところがあります。
「エレファントな群とリー代数」では「項書き換え」の「一階の項」を「一般マグマの多項式」として多項式のように扱う方法について考えました。論理プログラミングのデータとなる論理式も同様に扱うことができると考えられます。論理式がサーバーの状態を表すと考えて、無限に動くサーバーの状態を無限個の論理式として表したものを考えていきます。
F# でサーバー側で計算するプログラムとブラウザー側で計算するプログラムを比較すると、同じように記述することができることがわかります。この両方を同時に記述することがこの記事の目標となります。F# では「オブジェクト」で記述することもできるのでサーバーの状態の変化をオブジェクトの状態の変化として表したものも同様に記述できることを示すことも目標となっています。変数が変更不可能とすることもできるので、その場合も同様に記述できることを示すことも目標となっています。
F# のクラスの書き方がまだよくわからないので変なところがあるかもしれません。
C# から F# を呼び出す方法
「C#フォームとの連携 - F#マスターへの道 - atwiki(アットウィキ)」を参考にしましたが、.NET 6.0をインストールしないとできません。
C# での F# のリストの扱い
「F# の List は C# の List と違う | rksoftware」、「F# を知ってほしい - Qiita」を参考にしましたが、ちょっと違うようです。
Microsoft.FSharp.Collections.FSharpList<T>
Microsoft.FSharp.Collections.FSharpList<T>.Empty
Microsoft.FSharp.Collections.FSharpList<T>.Cons
を使うと良いようです。
コンストラクターの書き方
正しく動作していますがごちゃごちゃしています。
クラスの中に書ける関数
メンバーとして書けなかったので外に書いている関数があります。
演算子の定義
+、-、* は定義できましたが <= は定義できません。『実践 F# 関数型プログラミング入門』によると定義できそうなのですが定義できない理由はよくわかりません。
変数の変更がある場合の関数の呼び出し
正しく動作しています。
F# のコード
let Align0<'T> (xs: 'T list) (len: int) (z: 'T): 'T list = [ for xd in xs do yield xd for i in xs.Length .. len - 1 do yield z ] let ZipWith0<'T, 'U> (z: 'T) (f: 'T -> 'T -> 'U) (xs: 'T list) (ys: 'T list): 'U list = let xsx = Align0 xs (List.length ys) z let ysx = Align0 ys (List.length xs) z List.map2 f xsx ysx let FoldL0<'T, 'U> (z: 'U) (f: 'U -> 'T -> 'U) (xs: 'T list): 'U = let mutable a = z for x in xs do a <- f a x a let FoldR0<'T, 'U> (z: 'U) (f: 'U -> 'T -> 'U) (xs: 'T list): 'U = FoldL0 z f (List.rev xs) let EqualAndLess (x: int) (y: int): bool * bool = (x = y, x < y) type LongDecimal(ns1: int list, ns2: int list, e: int) = let mutable decimals_: int list = ns1 @ [ for i in 1 .. e -> 0 ] @ ns2 new() = LongDecimal([], [], 0) new(n: int) = LongDecimal([n], [], 0) new(n: int, e: int) = LongDecimal([], [n], e) new(ns: int list) = LongDecimal(ns, [], 0) new(ns: int list, e: int) = LongDecimal([], ns, e) new(n: int, ns: int list) = LongDecimal([n], ns, 0) member this.decimals = decimals_ static member CarryStep_ (carry_and_dec: int * LongDecimal) (x: int): int * LongDecimal = let (carry, dec): int * LongDecimal = carry_and_dec let newdec = new LongDecimal([(x + carry) % 10], dec.decimals, 0) ((x + carry) / 10, newdec) member this.Sum (n: int): LongDecimal = new LongDecimal((List.head this.decimals) + n, List.skip 1 this.decimals) member this.Decimalize (c: int, leave: bool): LongDecimal = let (carry, dec) = FoldR0 (c, new LongDecimal()) LongDecimal.CarryStep_ this.decimals if leave && carry <> 0 then dec.Sum(carry * 10) else dec member this.Shift (e: int): LongDecimal = new LongDecimal(this.decimals, e) member this.Product (xd: int): LongDecimal = new LongDecimal(List.map (fun (yd: int) -> xd * yd) this.decimals) member this.CollectShiftMult (y: LongDecimal): LongDecimal list = [ let mutable e: int = 0 for xd in this.decimals do yield y.Product(xd).Shift(e) e <- e + 1 ] static member (+) (x: LongDecimal, y: LongDecimal): LongDecimal = (new LongDecimal(ZipWith0 0 (fun a b -> a + b) x.decimals y.decimals)).Decimalize(0, true) static member (-) (x: LongDecimal, y: LongDecimal): LongDecimal = let yds = Align0 y.decimals (List.length x.decimals) 0 (new LongDecimal(ZipWith0 0 (fun a b -> a + b) x.decimals (List.map (fun a -> 9 - a) yds))).Decimalize(1, false) static member (*) (x: LongDecimal, y: LongDecimal): LongDecimal = List.fold (fun a b -> a + b) (new LongDecimal()) (x.CollectShiftMult y) static member LessOrEqual (x: LongDecimal, y: LongDecimal): bool = let equal = List.forall (fun b -> b) (ZipWith0<int, bool> 0 (fun a b -> a = b) x.decimals y.decimals) if equal then false else let get_not_eq (eq, lt) = not eq let (eq, lt) = List.find get_not_eq (ZipWith0 0 EqualAndLess x.decimals y.decimals) lt member this.Print() = let mutable res: string = ""; let mutable count: int = 0; for d in this.decimals do if count = 1 then res <- res + "." res <- res + d.ToString(); count <- count + 1 res type Numbers(number: LongDecimal, square_difference: LongDecimal, scale: int) = let mutable current_digit = 0 let mutable number: LongDecimal = number let mutable square_difference: LongDecimal = square_difference let mutable scale: int = scale member this.CurrentDigit: int = current_digit member this.GetNextDecimalDigit: int = let two: LongDecimal = new LongDecimal(2) current_digit <- -1 for dd = 9 downto 0 do if current_digit < 0 then let zd: LongDecimal = new LongDecimal(dd, scale) let number_sq_diff: LongDecimal = number * zd * two + zd * zd if LongDecimal.LessOrEqual (number_sq_diff, square_difference) then number <- number + zd; square_difference <- square_difference - number_sq_diff scale <- scale + 1 current_digit <- dd current_digit type NumbersGenerator() = let GenerateDecimal() = let number: LongDecimal = new LongDecimal() let square_difference: LongDecimal = new LongDecimal(3) let nums: Numbers = new Numbers(number, square_difference, 0) seq { while true do yield nums.GetNextDecimalDigit } let generator = GenerateDecimal().GetEnumerator() member this.GetNextDecimalDigit = let b = generator.MoveNext() generator.Current type NumbersServer() = let mutable current_numbers: Numbers = new Numbers(new LongDecimal(), new LongDecimal(), 0) let GenerateDecimalServer() = let number: LongDecimal = new LongDecimal() let square_difference: LongDecimal = new LongDecimal(3) current_numbers <- new Numbers(number, square_difference, 0) seq { while true do yield current_numbers } let generator_server = GenerateDecimalServer().GetEnumerator() member this.GetNumbers: Numbers = let b = generator_server.MoveNext() generator_server.Current member this.SetNumbers(numbers: Numbers): unit = current_numbers <- numbers member this.Numbers with get(): Numbers = this.GetNumbers and set(value: Numbers) = this.SetNumbers value