DhallでFizzBuzzしよう
設定ファイルのアーミーナイフ的言語 Dhall
Dhallは便利な言語ですが、再帰を行う関数を許容しないため、フィルターなどを書くときに一癖あります。
書く時のコツをFizzBuzzを例に紹介したいと思います。
FizzBuzzは3の倍数の時Fizz
、5の倍数の時Buzz
両方に一致するとき(15の倍数の時)FizzBuzz
と表示する、言語の文法解説などで使われるプログラムです。
Dhallで面倒なのは倍数を確認する部分で、標準的な機能のPrelude
には割り算や余りを求める関数がありません。そのため実現には工夫必要になります。
データを考える
プログラムで表現する状態は、Fizz、Buzz、FizzBuzz、普通の数字の4つです。HaskellやPureScriptのデータ型はUnion
と呼ばれる方法で表現します。ちなみにV10から表現方法が変化しましたので、昔のものをコピペしても動かないことがあります。
let FBdata : Type = <Fizz : Natural | Buzz : Natural | FizzBuzz : Natural | Normal : Natural>
鍵かっこで囲っている以外はHaskellライクなので、見慣れた感じです。
最終的に文字列になるので、文字列に変換する関数を定義します。
let FBdata/show
= λ(d : FBdata)
→ merge
{ Fizz = λ(t : Natural) → "Fizz"
, Buzz = λ(t : Natural) → "Buzz"
, FizzBuzz = λ(t : Natural) → "FizzBuzz"
, Normal = λ(n : Natural) → Natural/show n
}
d
merge
は、組込み関数として使用でき、パターンマッチを書きたいときに使用します。case of
構文の代わりぐらいの認識ですが、もっと面白い使い方の記事とかがあれば私が喜びます。
倍数チェック
プログラムの重要な部分である倍数をチェックする部分です。戦略としてはチェックする数字がFizzBuzzの倍数になっているかを見るのではなく、3の倍数及び5の倍数のListを必要なだけ作成して、チェックする数字がListに含まれるかを確認します。
まず、等差数列を作成する関数を書きます。
let Prelude = https://raw.githubusercontent.com/dhall-lang/dhall-lang/v10.0.0/Prelude/package.dhall
let numlist
= λ(n : Natural)
→ λ(pitch : Natural)
→ Prelude.List.generate n Natural (λ(x : Natural) → x * pitch)
最新のPrelude
はコンパイルが通らなかったので、V10のものを指定しています。
チェック関数は、1つの数字を渡してその数字が、Fizzリスト、Buzzリストに含まれるかを確認します。両方に該当する場合、FizzBuzzとして値を返します。
let check
= λ(n : Natural)
→ let fizzy = Prelude.List.any Natural (Prelude.Natural.equal n) (numlist n 3)
let buzzy = Prelude.List.any Natural (Prelude.Natural.equal n) (numlist n 5)
let checked =
if fizzy && buzzy then FBdata.FizzBuzz n
else if fizzy && Prelude.Bool.not buzzy then FBdata.Fizz n
else if Prelude.Bool.not fizzy && buzzy then FBdata.Buzz n
else FBdata.Normal n
in checked
確認のたびにnまでのFizzリスト、Buzzリストを生成しているので、巨大な数を確認する場合は、ほかの方法をとったほうが良いと思います。
出力
今までの関数を繋げて完了です。必要なだけの長さのリストを渡して、各要素ごとにチェック関数を適用させ、FBdataのリストを作成します。FBdata/show
を通して、文字列の読める形にします。
in let fizzbuzz = Prelude.List.map Natural FBdata check (numlist 20 1)
in Prelude.List.map FBdata Text FBdata/show fizzbuzz
すべてまとめて以下のようになります。
let Prelude = https://raw.githubusercontent.com/dhall-lang/dhall-lang/v10.0.0/Prelude/package.dhall
let FBdata : Type = <Fizz : Natural | Buzz : Natural | FizzBuzz : Natural | Normal : Natural>
let FBdata/show
= λ(d : FBdata)
→ merge
{ Fizz = λ(t : Natural) → "Fizz"
, Buzz = λ(t : Natural) → "Buzz"
, FizzBuzz = λ(t : Natural) → "FizzBuzz"
, Normal = λ(n : Natural) → Natural/show n
}
d
let numlist
= λ(n : Natural)
→ λ(pitch : Natural)
→ Prelude.List.generate n Natural (λ(x : Natural) → x * pitch)
let check
= λ(n : Natural)
→ let fizzy = Prelude.List.any Natural (Prelude.Natural.equal n) (numlist n 3)
let buzzy = Prelude.List.any Natural (Prelude.Natural.equal n) (numlist n 5)
let checked =
if fizzy && buzzy then FBdata.FizzBuzz n
else if fizzy && Prelude.Bool.not buzzy then FBdata.Fizz n
else if Prelude.Bool.not fizzy && buzzy then FBdata.Buzz n
else FBdata.Normal n
in checked
in let fizzbuzz = Prelude.List.map Natural FBdata check (numlist 20 1)
in Prelude.List.map FBdata Text FBdata/show fizzbuzz
実行させれば、ちゃんと実行できていることがわかります。
> cat fizzbuzz.dhall | dhall-to-json
["0","1","2","Fizz","4","Buzz","Fizz","7","8","Fizz","Buzz","11","Fizz","13","14","FizzBuzz","16","17","Fizz","19"]
Dhallの関数を使用することで、手で書くには面倒な記載を自動化することができます。逆に関数で記載が難しいような場合には、べた書きしてしまえば良いため、導入の心理的な障壁も小さいです。いろいろな出力先があるため、ちょっとでも、一部だけでも、一回だけでも使ってみてください。
Twitterをやってますので、もし宜しければフォローしてみてください。@noolbar
Author And Source
この問題について(DhallでFizzBuzzしよう), 我々は、より多くの情報をここで見つけました https://qiita.com/noolbar/items/6456001529bdbb739aa7著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .