Kotlin tailrec とは(超簡単解説)

Kotlin(コトリン)の勉強を始めたばっかりです。6年ぐらいJavaをやってて、まぁ、今度のAndroidアプリはKotlinでやってみるか、ということで始めました。

本を読んでいて、次のようなサンプルコードに出会いました。

    tailrec private fun gcd(a: Int, b: Int): Int =
        if (b == 0) a
        else gcd(b, a % b)

tailrecとはなんぞや!!

Kotlin公式サイトで見ます。残念ながら現時点では英語しかないらしい…。

https://kotlinlang.org/docs/reference/functions.html#tail-recursive-functions

簡単に、意訳しますね!

tailrecの修飾子がついた関数は、条件を満たせば、再帰関数をスタックオーバーフローの心配なく使える。

条件は

・その関数自体の呼び出しが、関数の最後である
・try catchブロックの中ではない

だそうです。

以上が、超簡単なtailrecの話でした。以下は、もうちょっと詳しく補足です。
—————————————————-

そもそも、tailrecの日本語が「末尾再帰」なのか「末尾再帰最適化」なのか、よくわかりませんが、再帰関数の、返り値が末尾で再帰する場合、Kotlinの場合はJVMがいい感じに処理してくれるということです。

何がいい感じなのかというと、再帰関数というのはスタックオーバーフローを起こしやすいのです。

自分自身を参照して、ぐるぐる回るので、なかなかストップ条件にいたらない場合、そうなりがちですね。

しかし、 末尾再帰最適化 では、それを普通のwhileループみたいに内部で書き換えてくれるので、スタックオーバーフローの心配がなくなるのです。

すごいー。

「じゃあ、最初からwhileとかで書けばいいじゃん」

という人もいると思います。

♪違う違う~ そうじゃ、そうじゃなーいー

再帰関数はあまりJavaとかでは出てこないですけども、使えると便利なシーンがあります。同じことを何度か繰り返す場合ですね。

その時に、

「ああー この入力値がもしかしてバカでかかったらスタックオーバーフローで止まってしまうのでは…」

と心配をしなくていいのは、素晴らしいことなんです。

2次元のNumpyの行列で、行と列に1個ずつしか1が存在しないかどうか確かめるサンプルコード

あんまり大した話じゃないんですけど(大したことを書いたことがないですが(^_^;))

次のような、2次元のNumpyの配列(ndarray)があった時に

pieces = np.array([
        [1, 0, 0],
        [0, 1, 0],
        [0, 0, 1]
        ])

行、列のどちらにも一個しか1が存在しないのが正しい状態で、2個以上の場合は、エラーを出す、という必要がありました。

import numpy as np

pieces = np.array([
        [1, 0, 0],
        [0, 1, 0],
        [0, 0, 1]
        ])

list1 = np.count_nonzero(pieces, axis=0)
print(list1)

list2 = np.count_nonzero(pieces, axis=1)
print(list2)

morethan_one_column = [i for i in list1 if i > 1 ]
print(morethan_one_column)

if morethan_one_column:
        print("あかーん")

morethan_one_row = [i for i in list2 if i > 1 ]
print(morethan_one_row)

if morethan_one_row:
        print("あかーん")

one hotなどで、行の方向に1個だけしか存在してほしくない、という状況はえてしてあると思います。

私の場合は、列の方向にも1個だけしか存在してほしくない、という状況ですけど、行の場合だけなら上記の

np.count_nonzero(pieces, axis=1)

だけでコト足りてます。

axisという引数は、軸ですが、軸の数え方、いつも間違いそうになりますけど、この2次元配列の場合、縦に並んでいるのが0軸です!!

下記にも count_nonzero は紹介してまして、実際のところ、下記の記事みてやり方思い出したって次第です。


NumPy 0じゃない要素をカウントする

Python

How to build mapbox-gl-js stand-alone on Windows

実質作業メモ。Windowsでの話です。

追記

ビルド作業が初めてではない(下記本編の内容を実施済み等)場合、ツール群のインストールは不要なため、下記手順のみ行う。

  1. cmd開いて cd mapbox-gl-js (ディレクトリ移動)して
  2. SET MAPBOX_ACCESS_TOKEN={YOUR MAPBOX ACCESS TOKEN} して
  3. yarn install して(必要なら)
  4. ファイル生成
    yarn run build-prod-min → dist/mapbox-gl.js
    yarn run build-css → dist/mapbox-gl.css

本編

下記のページに手順が書いてあるので実施していきます。

mapbox-gl-js/CONTRIBUTING.md at master · mapbox/mapbox-gl-js · GitHub

必要なツール群をインストール

Install gitnode.js (version 4 or greater), yarnnpm and node-gyp.

と書いてあるので、それぞれインストールしていきましょう。

git

もう入ってた。

node.js

ダウンロード | Node.js からWindows用のインストーラーをDLして実行して終わり。

Yarn 

Installation | Yarn からWindows用のStable(安定版) ver.のインストーラーをDLして実行して終わり。

npm and node-gyp.

nodejs-guidelines/windows-environment.md at master · microsoft/nodejs-guidelines · GitHub に詳しい。

  1. Windows PowerShell を管理者権限で起動
  2. npm install -g windows-build-tools を実行し、
    windows-build-tools をインストール
    ※トラブルシューティング情報も上記ページに記載あり
  3. Visual Studio Build Tools あるいは Visual Studio 2017 Community より、Visual C++ ビルド環境をインストール
  4. https://www.python.org/downloads/ よりPython2.7をインストール
  5. npm config set python python2.7 を実行
  6. npm config set msvs_version 2017 を実行

リポジトリをクローンする

git clone git@github.com:mapbox/mapbox-gl-js.git とコマンドを打ってもいいし、適当なGitHubクライアントでCloneしてもいい。

※特定のバージョンや安定版がほしい場合は、下記ページからzip等でファイルを取得する。
https://github.com/mapbox/mapbox-gl-js/releases

node moduleの依存関係をインストールする

  1. cd mapbox-gl-js (ディレクトリ移動)して
  2. yarn install を実行してインストール

warning "@mapbox/dr-ui > @mapbox/mapbox-gl-supported@1.4.1" has unmet peer dependency "mapbox-gl@>=0.32.1 <2.0.0". とかみたいなエラーが出たら、cmdを閉じて開き直してインストールコマンドを再実行すると多分解決する。

headless-gl の依存関係をインストールする

依存関係は下記。  https://github.com/stackgl/headless-gl#windows

  • Python 2.7
    ※ 先ほどの手順でインストール済
  • Microsoft Visual Studio
    ※ Visual C++ ビルド環境がほしいだけなので先程の手順でインストール済
  • d3dcompiler_47.dll
    c:\windows\system32に入っているのでOK
  • ES6 (モダンnode.js向け。オプション)
    ※ オプションなので多分不要

最終的にやりたいことは下記コマンドの通り。
copy node_modules/headless-gl/deps/windows/dll/x64/*.dll c:\windows\system32

headless-glをクローンする

stackgl/headless-gl: 🎃 Windowless WebGL for node.js をクローンする。

※特定のバージョンや安定版がほしい場合は、下記ページからzip等でファイルを取得する。
https://github.com/stackgl/headless-gl/releases

dllファイルをコピー

headless-gl\deps\windows\dll\x64\以下のdllファイルをc:\windows\system32 にコピーする。

ビルドに使うデバッグサーバの用意

mapboxアクセストークンの取得

  1. Mapbox のアカウントを作成
  2. Account | Mapbox > Tokens > Create a token からアクセストークンを作成
  3. Tokens にて、作成したトークンをコピー

デバッグサーバ起動確認

  1. cmdを起動して cd mapbox-gl-js (ディレクトリ移動) して
  2. SET MAPBOX_ACCESS_TOKEN={YOUR MAPBOX ACCESS TOKEN} を実行
    ※ここでSETした環境変数は、このセッションでのみ有効。cmdを終了すると消去される。
  3. yarn run start-debug を実行

デバッグサーバ http://localhost:9966/debug にアクセスできることを確認する。

スタンドアロンビルドを作成

以下のコマンドを実行する。
yarn run build-prod-min
yarn run build-css

dist/mapbox-gl.jsdist/mapbox-gl.css が生成されるので確認する。
HTMLではこの2つのファイルを読み込んで利用する。

余談

yarn run build-prod-min 実行時の出力が下記である。
なにやら警告?エラー?がいっぱい出ている。

mapbox-gl-js>yarn run build-prod-min
yarn run v1.19.0
$ rollup -c --environment BUILD:production,MINIFY:true

src/index.js, src/source/worker.js → rollup/build/mapboxgl...
(!) Circular dependency: src\util\ajax.js -> src\util\mapbox.js -> src\util\ajax.js
(!) Circular dependency: src\style-spec\expression\parsing_context.js -> src\style-spec\expression\compound_expression.js -> src\style-spec\expression\parsing_context.js
(!) Circular dependency: src\style-spec\validate\validate.js -> src\style-spec\validate\validate_function.js -> src\style-spec\validate\validate.js
(!) Circular dependency: src\style-spec\validate\validate.js -> src\style-spec\validate\validate_function.js -> src\style-spec\validate\validate_object.js -> src\style-spec\validate\validate.js
(!) Circular dependency: src\style-spec\validate\validate.js -> src\style-spec\validate\validate_function.js -> src\style-spec\validate\validate_array.js -> src\style-spec\validate\validate.js
(!) Circular dependency: src\style-spec\validate\validate.js -> src\style-spec\validate\validate_layer.js -> src\style-spec\validate\validate_paint_property.js -> src\style-spec\validate\validate_property.js -> src\style-spec\validate\validate.js
(!) Circular dependency: src\style-spec\validate\validate.js -> src\style-spec\validate\validate_layer.js -> src\style-spec\validate\validate.js
(!) Circular dependency: src\style-spec\validate\validate.js -> src\style-spec\validate\validate_light.js -> src\style-spec\validate\validate.js
(!) Circular dependency: src\style\style_layer\symbol_style_layer.js -> src\data\bucket\symbol_bucket.js -> src\style\style_layer\symbol_style_layer.js
(!) Circular dependency: src\geo\lng_lat.js -> src\geo\lng_lat_bounds.js -> src\geo\lng_lat.js
(!) Circular dependency: src\source\tile.js -> src\data\feature_index.js -> src\source\source_state.js -> src\source\tile.js
(!) Circular dependency: src\index.js -> src\ui\map.js -> src\style\style.js -> src\util\global_worker_pool.js -> src\util\worker_pool.js -> src\util\browser\web_worker.js -> src\index.js
created rollup/build/mapboxgl in 11.7s

rollup/mapboxgl.js → dist/mapbox-gl.js...
created dist/mapbox-gl.js in 2s
Done in 14.43s.

「Circular dependency」って何??
直訳すると「循環依存」 となるのでやんわりと察した。
AがBに依存し、BはAに依存しているのでウロボロスって話。

警告は出ているけど mapbox-gl.js は生成されているし、HTML側で読み込んでも(おそらく)正常に動いている(ように見える)。

とりあえず様子見。

PHPで複数のコンストラクタを設定する

PHPでは複数のコンストラクタを、例えば引数の数に応じて使い分けるということができません。(´ω`)

下記みたいにやりたいじゃないですか。

<?php
class A
{
   function __construct()
    {
    }
   
    function __construct($a1)
    {
        echo('引数が1個のコンストラクタ');
    }
   
    function __construct($a1,$a2)
    {
        echo('引数が2個のコンストラクタ');
    }
   
}


でもできません。(´ω`)

「そんなの不便じゃーん」

もちろんそうですが、ググっても「できません」とかあまりいいやり方出てこなかったので、下記のPHP本家サイトに載っているコードですが、再掲させて頂きます。

https://www.php.net/manual/ja/language.oop5.decon.php

みんな!PHPの本家マニュアルサイトで大事なのは、下の方にある、

User Contributed Notes

だよ!ここ読むと、いいこと書いてあるので読み得です。
英語なのが玉にキズ。(´ω`)

<?php
class A
{
  //要はコンストラクタを使って、引数の数でメソッド振り分けしている
    function __construct()
    {
        $a = func_get_args();
        $i = func_num_args();
        if (method_exists($this,$f='__construct'.$i)) {
            call_user_func_array(array($this,$f),$a);
        }
    }
   
    function __construct1($a1)
    {
        echo('__construct with 1 param called: '.$a1.PHP_EOL);
    }
   
    function __construct2($a1,$a2)
    {
        echo('__construct with 2 params called: '.$a1.','.$a2.PHP_EOL);
    }
   
    function __construct3($a1,$a2,$a3)
    {
        echo('__construct with 3 params called: '.$a1.','.$a2.','.$a3.PHP_EOL);
    }
}
$o = new A('sheep');
$o = new A('sheep','cat');
$o = new A('sheep','cat','dog');

// results:
// __construct with 1 param called: sheep
// __construct with 2 params called: sheep,cat
// __construct with 3 params called: sheep,cat,dog
?>

TestFlightで「テストするAppがありません」と表示される

App Store Connectで登録済のApple IDでサインインしているiPadでTestFlightを開くと、「テストするAppがありません」と表示されてしまいテスト用アプリをインストールできない状態でした。

設定から一旦サインアウトしてサインインしなおしてみても解決しません。

このiPadではAppleIDメール受信の設定をしていなかったので、AppleIDのメールを受信できるように設定し、App Store Connect→TestFlight→App Store Connect Usersから一旦削除・再登録して招待メールを再送信しなおしました。

届いた招待メールから「Views in TestFlight」をクリックすると、TestFlightアプリの設定が更新されたようで、無事テスト用アプリがインストールできるようになりました。