🧐

LISP派生のClojure言語でFizzBuzz書いてみた

2021/08/01に公開

こんにちは!
ついこの間、はてブでLisp派生のClojureという言語を見かけたもので、
他Lisp系と違う書き心地を知りたく興味本位で入門してみました!

https://clojure.org/

Clojureについて

Lisp派生の言語であるため、式や構文の特徴は他の方言(派生言語)と基本的には同じです。
(処理系の振る舞いや組み込まれているSymbolに違いがあるとは思いますが。

とりあえずLisp派生言語で木構造やソートアルゴリズムのプログラムを実装したことがあれば躓くことは無いと思います。

関数の定義方法が面白い

Common Lispの場合

(defun message()
  (print "Hello, World!")
 )

Schemeの場合

(define (message)
  (display "Hello, World!")
)

以前にSchemeでバブルソート書きました
https://github.com/huuyafwww/algorithms/blob/master/bubbleSort.rkt

Clojureの場合(2種類ある)

省略しない場合

(def message
  (fn [opts]
    (println "Hello World!")
  )
)

define functionの省略形

(defn message [opts]
  (println "Hello World!")
)

個人的には前者のほうが明示的で好みでした🙌

細かいですが、AutoLISPにあるようなquit相当のSymbolや、
Common Lispの空return(break変わりのSymbol)相当の処理がClojureにはあるのかなと公式リファレンスを探ってみたのですが、見当たらなかったです(見つけれていないだけなのかも?

環境構築について

macで環境構築しました。

brew install clojure/tools/clojure

# インストール後オプション無しで実行して組み込みのライブラリ等を取り込む
clj

VS Codeであればこの拡張機能を入れておくとClojureのSystax等いい感じに制御してくれます

公式サイト
https://calva.io/

拡張機能
https://marketplace.visualstudio.com/items?itemName=betterthantomorrow.calva

書いてみた

楽しさを味わうためにdottimesやmapでは無く敢えて再帰処理でFizzBuzzを記述しています。

また、他言語と同じく条件式は左式から評価されていくため、
真の条件を踏んだ時点で条件式の評価が決定されます。

エントリーポイントはrun関数です。

(ns fizzbuz)

(def stdout_message
  (fn [num]
    (println
      (cond
        (and (= (mod num 3) 0) (= (mod num 5) 0 )) "FizzBuzz"
        (= (mod num 3) 0) "Fizz"
        (= (mod num 5) 0) "Buzz"
        :else num
      )
    )
  )
)

(def recursive_number_to_limit
  (fn [num limit_num]
    (stdout_message num)
    (if (< num limit_num)
      (recursive_number_to_limit (+ num 1) limit_num)
    )
  )
)

(def run
  (fn [opts]
    (let [num 1
      limit_num 100]
      (recursive_number_to_limit num limit_num)
    )
  )
)

ディレクトリ構造について

$ tree
.
└── clj
    └── src
        └── fizzbuz.clj

実行する

$ cd clj
$ clj -X fizzbuz/run

色んな言語で同じ立ち振舞を再現してみる

この記事を見てくださっているの方の中にLispを書いたことが無いという方も居ると思うので、
同じ振る舞いを他の言語でも再現してみました!
(先程挙げたFizzBuzzコードから成るべくアプローチがずれないように再現しています。

C
void stdout_message(int num){
  if (
    num % 3 == 0 && num % 5 == 0
  ) {
    printf("FizzBuzz");
  } else if( num % 3 == 0 ) {
    printf("Fizz");
  } else if( num % 5 == 0 ){
    printf("Buzz");
  } else{
    printf("%d", num);
  }
}

void recursive_number_to_limit(int num, int limit_num) {
  stdout_message(num);
  if (num < limit_num) {
    recursive_number_to_limit(num + 1, limit_num);
  }
}

int main(void) {
  int num = 1;
  int limit_num = 100;
  recursive_number_to_limit(num, limit_num);
  return 0;
}
PHP
<?php

function stdout_message(
  int $num
){
  if (
    $num % 3 === 0 && $num % 5 === 0
  ) {
    echo "FizzBuzz";
  } else if( $num % 3 === 0 ) {
    echo "Fizz";
  } else if( $num % 5 === 0 ) {
    echo "Buzz";
  } else {
    echo (string) $num;
  }
}

function recursive_number_to_limit(
  int $num,
  int $limit_num,
){
  stdout_message($num);
  if ($num < $limit_num) {
    recursive_number_to_limit($num + 1, $limit_num);
  }
}

function run(){
  $num = 1;
  $limit_num = 100;
  recursive_number_to_limit($num, $limit_num);
}
Ruby
def stdout_message(num)
  if num % 3 == 0 and num % 5 == 0
    puts "FizzBuzz"
  elsif num % 3 == 0
    puts "Fizz"
  elsif num % 5 == 0
    puts "Buzz"
  else
    puts num
  end
end


def recursive_number_to_limit(num, limit_num)
  stdout_message(num)
  if num < limit_num
    recursive_number_to_limit(num + 1, limit_num)
  end
end

def run()
  num = 1
  limit_num = 100
  recursive_number_to_limit(num, limit_num)
end
Python
def stdout_message(num: int) :
  if num % 3 == 0 and num % 5 == 0 :
    print("FizzBuzz")
  elif num % 3 == 0 :
    print("Fizz")
  elif num % 5 == 0 :
    print("Buzz")
  else :
    print( str(num) )


def recursive_number_to_limit(num: int, limit_num: int) :
  stdout_message(num)
  if num < limit_num :
    recursive_number_to_limit(num + 1, limit_num)


def run() :
  num = 1
  limit_num = 100
  recursive_number_to_limit(num, limit_num)
JavaScript
const stdout_message = num => {
    if (num % 3 === 0 && num % 5 === 0) {
        console.log("FizzBuzz");
    } else if(num % 3 === 0) {
        console.log("Fizz");
    } else if(num % 5 === 0) {
        console.log("Buzz");
    } else {
        console.log(num);
    }
};

const recursive_number_to_limit = (num, limit_num) => {
    stdout_message(num);
    if (num < limit_num) {
        recursive_number_to_limit(num + 1, limit_num);
    }
};

const run = () => {
    num = 1;
    limit_num = 100;
    recursive_number_to_limit(num, limit_num);
};

まとめ

この記事を書いてるときにringというClojureのHTTPサーバライブラリを見つけました!

https://github.com/ring-clojure/ring

サーバサイドでLispをいじったことが無いのでなんか楽しそうだなぁと好奇心が湧きました笑

https://github.com/ring-clojure/ring/wiki/Creating-responses

レスポンスの記述が視覚的で分かりやすいのも魅力的でした!

近々触ってみようと思います!

Discussion