saba1024のブログ

どうでも良い思いついた事とかプログラミング関係のメモとか書いていきます。

RustでFizzBuzz

fn fizz_buzz(n: i32) -> String {
    match (n % 3, n % 5) {
        (0, 0) => "FizzBuzz".to_string(),
        (0, _) => "Fizz".to_string(),
        (_, 0) => "Buzz".to_string(),
        (_, _) => n.to_string(),
    }
}

fn main() {
    let result = (1..101).map(|x| fizz_buzz(x)).collect::<Vec<_>>();
    println!("{:?}", result);
}

Scalaでパターンマッチを勉強した時には、まぁ便利なのかな?ぐらいの感覚でしたが、実際に簡単なFizzBuzzででも、自分で使ってみるとなるほどこれは良い、というのが実感できますね。

Rustでクロージャを使ってカウンターを実装するサンプル

クロージャのお勉強用のお題としてよくある、「ある関数から、実行する度に値をインクリメントして返すクロージャを生成して返す」をRustで実装してみました。
RustのクロージャはちょっとApache Groovyなどと比べると癖が有って難しい印象です...(所有権とかが絡んでくるので)
理屈を理解することは当然大切ですが、この辺りはもう慣れっていうのも大事なのかな〜と思って最近は色々小さいコードをRustで書いて色々実験しています。

fn create_counter() -> Box<FnMut() -> i32> {
    // カウンタのデフォルト
    let mut x = 0;

    // 変数xはスタック上に確保されるので、moveを使ってxのコピーの所有権をクロージャに移してあげる。
    let clj = move || {
        x += 1;
        x
    };

    // Rustは、クロージャを返す場合にはBoxで包んで返して上げる必要がある。
    Box::new(clj)
}

fn main() {

    let mut counter1 = create_counter();
    debug_assert!(counter1() == 1);
    debug_assert!(counter1() == 2);
    debug_assert!(counter1() == 3);

    let mut counter2 = create_counter();
    debug_assert!(counter2() == 1);
    debug_assert!(counter2() == 2);
    debug_assert!(counter2() == 3);
    debug_assert!(counter2() == 4);
    debug_assert!(counter2() == 5);

    // 当然別のクロージャが持っている環境(変数)に影響なし。
    debug_assert!(counter1() == 4);
    debug_assert!(counter1() == 5);
}

Apache Groovy 2.6でJava8の文法が使える!

表題の通りなのですが、ParrotブランチでGroovy3を垣間見るで示されている内容が、Groovy2.6(2017/09/06時点ではalpha-1)の時点で利用できるようになりまりた。
ざっくりというと、Java8の新構文(lambdaとか)や、Java7で導入されたtry-with-resourcesが利用できるようになったりなど、Groovyがより一層Javaとの親和性を高める内容になっています。

まだアルファ版なので今後変化する可能性が有りますが、具体的に何が変るのかに関しては、上記の記事や、公式のRelease notes for Groovy 2.6に網羅されています。

現在ベータ版が提供されているGroovy2.5ではまだ新パーサは提供されていないのでこの内容は該当しません。
Groovy2.6からは、オプションとして-Dgroovy.antlr4=trueを指定してGroovyを利用することで、Java8のlambda文などがそのまま利用できるようになります。
そして、Groovy3からはデフォルトでこのオプションがオンにる予定です。

実際にSDKMANで、2.6-alpha1を試してみました。
なお、GroovyConsoleを使いたかったので、-Dgroovy.antlr4=trueJAVA_OPTSに格納しています。

# 先ずはインストール
[koji:~]$ sdk install groovy 2.6.0.alpha-1
[koji:~]$ sdk use groovy 2.6.0-alpha-1

# JAVA_OPTSに今回のオプションを追加する
[koji:~]$ echo $JAVA_OPTS

[koji:~]$ export JAVA_OPTS="-Dgroovy.antlr4=true"
[koji:~]$ echo $JAVA_OPTS                        
-Dgroovy.antlr4=true
[koji:~]$ 
[koji:~]$ groovyConsole&

以下のように、lambda式がそのまま使えます!

def primes = [1,2,3,4,5]
Collections.sort(primes, (a, b) -> { return b - a; });
assert primes == [5,4,3,2,1]

これで再びGroovyがJavaとほぼ同等、と胸を張って言えるようになります。
が、Java9の登場と今後のJava言語のロードマップが示されたことで、今後はGroovyに限らずJVM上で動いてる各種JVM言語に様々な影響を及ぼすと考えられます。
それに関しても色々調査していきたいと思います。

Apache Groovyで文章を縦書にする

String str = "Groovyを使い、textを縦書に変換するコマンドを書いたので、今後活用したい。と思う次第であります。"
Integer maxCharacters = 8
List<String> eachLines = str.toList().collate(maxCharacters)
eachLines.eachWithIndex {List<String> line, Integer i ->
    if (line.head() in ["。", "、"]) {
        eachLines[i-1] << line.head()
        (i ..< eachLines.size()).each {
            eachLines[it] = eachLines[it].tail() + eachLines[it+1]?.head()
        }
    }
}

(0..<eachLines.collect{it.size()}.max()).each {def line ->
    println eachLines.collect {
        " ${it[line]?: ' '}"
    }.reverse().join("")
}

実行結果は、

 ま と 今 ン 書 い G
 す 思 後 ド に 、 r
 。 う 活 を 変 t o
   次 用 書 換 e o
   第 し い す x v
   で た た る t y
   あ い の コ を を
   り 。 で マ 縦 使

テストもないし副作用バリバリのコードだけど気にしない!
もし行の先頭に句読点(。か、)が来た場合にはちゃんと前の行の末尾に加えるようにはしています。

Groovyでお手軽にカレンダーを生成する

Groovyだと、2つの日付同士のレンジが..で生成できるので非常にお手軽にカレンダーを生成することが出来ます。
うるう年もちゃんと考慮されるので、例えば以下のようなことが出来ます。

String df = "yyyy/MM/dd"

// うるう年のある年
Date date1 = Date.parse(df, "2016/01/01")
Date date2 = Date.parse(df, "2016/12/31")

assert (date1..date2).size() == 366
List<Date> february2016 = (date1..date2).findAll {it.getAt(Calendar.MONTH) == 1}
assert february2016.size() == 29
assert february2016.last() == Date.parse(df, "2016/02/29")

// うるう年の無い年
Date date3 = Date.parse(df, "2017/01/01")
Date date4 = Date.parse(df, "2017/12/31")

assert (date3..date4).size() == 365
List<Date> february2017 = (date3..date4).findAll {it.getAt(Calendar.MONTH) == 1}
assert february2017.size() == 28
assert february2017.last() == Date.parse(df, "2017/02/28")

非常にお手軽ですね!

Nginx Unitを試してみた

最近リリースされた、Nginx Unitを試してみました。

インストール

こういう新しいツールを試すのにはやっぱりDockerですね!
とうことで、Docker上にNginx Unitをインストールして見ます。 今回はUbuntuで試してみました。

# デタッチモードで起動
sudo docker run -itd --name nginx-unit -p 8300:8300 ubuntu
# Dockerコンテナにアクセス
sudo docker exec -it nginx-unit bash

Dockerコンテナにアクセスできたら、まず必要なライブラリをインストールします。

root@88e2db6c51bc:~#apt-get update
root@88e2db6c51bc:~#apt-get install -y wget vim

インストールが完了したら、基本的には公式リポジトリのREADME通りに勧めるだけでOKです。
なお、今回はソースからコンパイルではなく、apt-getを使ってNginx Unitをインストールします。

root@88e2db6c51bc:~# cd
root@88e2db6c51bc:~# wget http://nginx.org/keys/nginx_signing.key
root@88e2db6c51bc:~# apt-key add nginx_signing.key
OK

/etc/apt/sources.listに以下の2行を追加。

deb http://nginx.org/packages/mainline/ubuntu/ xenial nginx
deb-src http://nginx.org/packages/mainline/ubuntu/ xenial nginx

これで準備完了です。後はapt-getで普通にインストールできます。

apt-get update
apt-get install unit

起動と停止

service unitd [start | stop]

Unit の設定

READMEを読む限り、全てAPI経由でNginxUnitサーバの設定を行うようです。
ということで、先ずはNginx Unitを起動しておきます。

service united start

Unitdが起動したら、以下のコマンドを実行してみます。

root@88e2db6c51bc:/var/lock# curl --unix-socket /run/control.unit.sock http://localhost/
{
        "listeners": {},
        "applications": {}
}

何やら空の値が帰ってきました。
NginxUnitでは、listenersapplicationsという2つの重要な概念があるようです。
指定したIPとPortにアクセスが来たら、指定したapplicationを実行する、という指定のlisteners、そして実際のアプリケーションの登録を行うapplications

API経由でJSONファイルを渡してあげることで登録できます。
まず、READMEにある通り、以下の内容のファイルを用意します。(とりあえずstart.jsonという名前にしましたが、何でもOKです)

{
     "listeners": {
         "*:8300": {
             "application": "blogs"
         }
     },
     "applications": {
         "blogs": {
             "type": "php",
              "workers": 20,
              "root": "/www/blogs/scripts",
              "index": "index.php"
         }
     }
}

そしてこれをlocalhostに投げてあげます。

root@88e2db6c51bc:~# curl -X PUT -d @/root/start.json --unix-socket /run/control.unit.sock http://localhost/                                                                                                                                                                    
{
        "success": "Reconfiguration done."
}

これで完了です。
実際に再度以下のように確認してみると、何やら値が設定されていることが確認できます。

root@88e2db6c51bc:~# curl --unix-socket /run/control.unit.sock http://localhost/
{
        "listeners": {
                "*:8300": {
                        "application": "blogs"
                }
        },

        "applications": {
                "blogs": {
                        "type": "php",
                        "workers": 20,
                        "root": "/www/blogs/scripts",
                        "index": "index.php"
                }
        }
}
root@88e2db6c51bc:~# 

PHP

では実際にコードを設置してみましょう!
先ほどのstart.jsonで、実は既にドキュメントルートを指定しています。(/www/blogs/scripts)
なので、先ずはそのディレクトリを作成します。

root@88e2db6c51bc:~# mkdir -p /www/blogs/scripts

で、後はそのディレクトリにPHPファイルを設置するだけでOKです。

echo "<?php phpinfo();?>" > /www/blogs/scripts/index.php

これで、http://localhost:8300/にアクセスすればphpinfoが表示されるはずです。

ちなみに、unitのインストールで必要なPHPのライブラリはインストールされているっぽい。楽ちん。

root@88e2db6c51bc:~# dpkg -l | grep php
ii  libphp-embed               1:7.0+35ubuntu6                       all          HTML-embedded scripting language (Embedded SAPI library) (default)
ii  libphp7.0-embed            7.0.22-0ubuntu0.16.04.1               amd64        HTML-embedded scripting language (Embedded SAPI library)
ii  php-common                 1:35ubuntu6                           all          Common files for PHP packages
ii  php7.0-common              7.0.22-0ubuntu0.16.04.1               amd64        documentation, examples and common module for PHP
root@88e2db6c51bc:~# 

感想

簡単に実行できました。
ただ、今一メリットを理解できていないのが現状です。
一つのNginx Unitさえあれば、一つのunitedプロセスで、様々な言語(現在だとPHP、Go、Pythonがサポートされている。そのうちJavaとNode.jsも予定)で実装されたサービスを動かせる、という特徴はありますが、これも今までそれぞれが持っていたアプリケーションサーバから乗り換えるほどのメリットなのか?と言われると微妙な感じです。
ただ、今回のPHPだけで考えてみると、今まではNginx(Web)とPHP-FPM(App)を別々の物として管理運用していましたが、Nginx Unitだとコレが一つに纏まる訳なので、運用面でのそういったメリットは確かにあるのかな?とぼんやりと考えています。

まぁとは言っても今回のようにPHPだけではなくて、他の言語のアプリケーションも同時にNginx Unit上で実際に運用してみて初めてメリットが見えてくるような気もしますので、Javaが正式にサポートされたタイミングなどで再度試してみて、また色々確認してみようかなと思います。

Rust 1.20の新機能 Associated constants(関連定数)

概要

最近Rustの勉強をしています。
今まで基本的にJavaApache Groovy畑で生きてきたので新しい概念が多くかなり苦戦しています。
さて、8月31日にRust1.20がリリースされました。この新しいバージョンでAssociated constants(関連定数)という機能が追加されました。
ざっくり言うと、既にあったAssociated functions(関連関数)の定数版という感じのようです。
まだまだ自身を持ってどうこう言えるほどRustを理解できているわけではありませんが、実際に使ってみたので以下のそのサンプルコードを示します。

実際のコード

// Associated constants with struct
struct Struct;
impl Struct {
    // associated constants
    const PI:f32 = 3.14;

    fn double(&self) -> f32 {
        Self::PI * 2.0
    }
}

// Associated constants with trait.
trait Trait {
    // associated constants
    const PI:f32;

    fn double(&self) -> f32 {
        Self::PI
    }
}
struct Circle;
impl Trait for Circle {
    const PI:f32 = 3.14;
}

fn const_via_associated_constants<T: Trait>(_v: T) -> f32 {
    T::PI * 2.0
}

fn const_via_method <T: Trait>(v: T) -> f32 {
    v.double() * 2.0
}

fn main () {
    // use direct
    debug_assert!(Struct::PI == 3.14);
    debug_assert!(Circle::PI == 3.14);

    // use direct via method.
    debug_assert!(Struct{}.double() == 6.28);

    // use from a function(direct and via method)
    debug_assert!(const_via_associated_constants(Circle{}) == 6.28);
    debug_assert!(const_via_method(Circle{}) == 6.28);

    println!("Rust 1.20");
}

参考

Announcing Rust 1.20