エレファント・ビジュアライザー調査記録

ビジュアルプログラミングで数式の変形を表すことを考えていくブロクです。

関数プログラミングと無限論理多項式(5)

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# 関数型プログラミング入門』によると定義できそうなのですが定義できない理由はよくわかりません。

変数の変更がある場合の関数の呼び出し

正しく動作しています。

イテレータ

C#イテレーターがほとんどそのまま使えるので良いです。

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