LoginSignup
2
1

More than 5 years have passed since last update.

「gumi Inc. が Erlang & Elixir Fest 2018 の会場で配布しているクイズ」をF#で

Last updated at Posted at 2018-06-16

元の問題(Elixir) https://gist.github.com/cooldaemon/35acccd20d226ee33b639b17156242dc

Mission2

walk 関数を隠ぺいする方法もありますが、今回はそのまま。

Gumimaze.fs
module Gumimaze =
    let read =
        let withIndex xs = Array.mapi (fun i x -> x, i) xs
        let lines = "maze.txt" |> System.IO.File.ReadAllLines
        seq {
            for line, y in withIndex lines do
                for c, x in withIndex (line.ToCharArray ()) ->
                    (x, y), c
        }
        |> Map.ofSeq

    let rec walk maze p walked pt =
        let walked = Map.add p true walked
        let aroundP =
            let x, y = p
            [x+1, y; x, y+1; x-1, y; x, y-1]
        let checkAndWalk p =
            match Map.containsKey p walked, Map.find p maze with
            | true, _   -> []
            | _   , 'W' -> []
            | _   , '1' -> walk maze p walked (pt + 1)
            | _   , 'G' -> [pt]
            | _   , _   -> walk maze p walked pt
        List.collect checkAndWalk aroundP

    let solve maze =
        let p = maze |> Map.pick (fun k v -> if v = 'S' then Some k else None)
        walk maze p Map.empty 0 |> List.max

    [<EntryPoint>]
    let main argv = 
        read |> solve |> printfn "%A"
        0

Mission3

MailboxProcessor クラスでアクターモデルにするのが、お題に沿った回答になるでしょうか。ただし、以下の実装では余計に遅くなります。

Gumimaze.fs
module GumimazeAgent
    open Microsoft.FSharp.Control
    open System.Threading.Tasks

    type ReducerMessage = Point of int | End
    type CounterMessage = Inc | Dec
    type WalkerMessage = Map<int*int, char> * (int * int) * Map<int*int, bool> * int;

    let answer = new TaskCompletionSource<int>()

    let reducer = MailboxProcessor<_>.Start(fun mb ->
        let rec mainLoop max = async {
            let! message = mb.Receive()
            match message with
            | Point pt -> return! mainLoop (if pt > max then pt else max)
            | _        -> return answer.SetResult(max)
        }
        mainLoop 0
    )

    let counter = MailboxProcessor<_>.Start(fun mb ->
        let rec mainLoop count = async {
            let! message = mb.Receive()
            let count =
                match message with
                | Inc -> count + 1
                | Dec -> count - 1
            if count > 0 then
                return! mainLoop count
            else
                End |> reducer.Post(ReducerMessage.End)
        }
        mainLoop 0
    )

    let rec walkerBody = fun (mb: MailboxProcessor<_>) -> async {
        let! msg = mb.Receive()
        let walked = Map.add msg.Position true msg.Walked
        let aroundP =
            let x, y = msg.Position
            [x+1, y; x, y+1; x-1, y; x, y-1]
        for p in aroundP do
            match Map.containsKey p walked, Map.find p msg.Maze with
            | true, _   -> ()
            | _   , 'W' -> ()
            | _   , '1' ->
                Inc |> counter.Post
                let walker = MailboxProcessor<_>.Start(walkerBody)
                (msg.Maze, p, walked, msg.Point + 1) |> walker.Post
            | _   , 'G' ->
                Point msg.Point |> reducer.Post
            | _   , _   ->
                Inc |> counter.Post
                let walker = MailboxProcessor<_>.Start(walkerBody)
                (msg.Maze, p, walked, msg.Point) |> walker.Post
        Dec |> counter.Post
    }
    let walker = MailboxProcessor<_>.Start(walkerBody)

    let withIndex xs =
        Array.mapi (fun i x -> x, i) xs

    let read =
        let lines = "maze.txt" |> System.IO.File.ReadAllLines
        seq {
            for line, y in withIndex lines do
                for c, x in withIndex (line.ToCharArray ()) ->
                    (x, y), c
        }
        |> Map.ofSeq

    let solve maze =
        let p = maze |> Map.pick (fun k v -> if v = 'S' then Some k else None)
        Inc |> counter.Post
        (maze, p, Map.empty, 0) |> walker.Post

    [<EntryPoint>]
    let main argv =
        read |> solve
        answer.Task.Result |> printfn "%A"
        0
2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1