7
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

[翻訳] Elixirで書く関数型FizzBuzz

Last updated at Posted at 2015-08-15

Michał Muskałaさんの2015年8月10日付のブログ記事Functional FizzBuzz in Elixirの翻訳です。

文中にもありますが、Erlangでちょっと変わったFizzBuzzの書き方をした人がいてそれをElixirに移植したという記事です。面白かったんで翻訳してみました。

普通は3または5またはその両方で割り切れるか剰余演算子で調べて Fizz/Buzz/FizzBuzz を出し分けるんで、パターンマッチと再帰(そもそもループがないから)のサンプルにしかならないんですけどね、FizzBuzz。関数型の例なのかよ!と言われるとどうなんでしょうか(笑)

あと、これ、Clojureでも同じように書けそうだなあ…と思ったらその通りで書けました(おまけ参照)


前回のSteven ProctorさんのErlang Thursday - 関数型 fizzbuzzからインスパイアされたんでどうやったら剰余演算子を使わないでFizzBuzzをElixirっぽく実装できるかどうかやってみました。

こんな感じかな:

defmodule FizzBuzz do
  def print(n) do
    generate
    |> Stream.take(n)
    |> Stream.each(&IO.puts/1)
    |> Stream.run
  end

  def generate do
    fizzes = Stream.cycle(["Fizz", "", ""])
    buzzes = Stream.cycle(["Buzz", "", "", "", ""])

    Stream.zip(fizzes, buzzes)
    |> Stream.map(&concat/1)
    |> Stream.with_index
    |> Stream.map(&translate/1)
    |> Stream.drop(1)
  end

  defp concat({f, b}), do: f <> b

  defp translate({"",   n}), do: to_string(n)
  defp translate({str, _n}), do: str
end

おおよそのところは見ての通りですけど、独自性もあるのでそこを説明します。Erlangでの実装の問題点のひとつとしてはcycleのような遅延評価のストリーム及び関数がないことだと思っていますが―ElixirではStreamモジュールがあるのでこれは問題になりません。一方でErlangではlists:zipwith/31が使えるので項目を合成して結合するのをいちどきに行うことが可能です。Elixirでは2ステップが必要です―まずStream.zip/2(またはStream.with_index/1)を使い、直後に結果のタプルをマップして必要なフォーマットにします。

Stream.drop/2の呼び出しはElixirのインデックスが0から始まるのにfizzbuzzの結果の最初の項目は1から始まって欲しいために必要となっています。これを避けるには第3のストリームを生成してそれが1から始まる番号を返すようにして、それを合成するのも手ですね:

def generate2 do
  fizzes  = Stream.cycle(["", "", "Fizz"])
  buzzes  = Stream.cycle(["", "", "", "", "Buzz"])
  indexes = Stream.iterate(1, &(&1 + 1))

  Stream.zip(fizzes, buzzes)
  |> Stream.map(&concat/1)
  |> Stream.zip(indexes)
  |> Stream.map(&translate/1)
end

どっちがより明快で理解しやすいかは私にはわかりかねますが…。


おまけ:Clojureで書いてみた

割と簡単に書けました。とはいえしばらくClojure書いてなかったからもっといいやり方がありそうですが…

(def fizzes (cycle ["" "" "Fizz"]))
(def buzzes (cycle ["" "" "" "" "Buzz"]))
(def indexes (iterate inc 1))
(defn translate [x,n]
  (cond (= x "") (str n)
        :else x
        )
  )

(defn print-fb [n]
  (take n (map #(translate %1 %2) (map #(str %1 %2) fizzes buzzes) indexes))
  )

  1. 例:lists:zipwith(fun(X,Y) -> X+Y end, [1,2,3], [4,5,6]).はリスト[5,7,9]を返します。lists:zip([1,2,3],[4,5,6]).が[{1,4},{2,5},{3,6}]という2項タプルのリストを返すんでそのタプルの2つの要素を第1引数の無名関数で演算する感じです。

7
8
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
7
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?