【Kotlin】通常ではfor文で回せないものをfor文っぽく扱う

Kotlinでは、浮動少数やBigIntegerなどはそのままだとfor文に入れて回すことができません。
こういったものはgenerateSequenceを使ってシーケンスにすることで、for文のように回すことができます。

やり方

以下はgenerateSequenceを使って階乗を実装したものです。
generateSequence{ }内の処理が値の更新と終了条件の判定です。takeIf { }内が終了条件です。

fun fact(n: BigInteger): BigInteger{
    var i = n
    var ans = BigInteger.ONE
    generateSequence { (i--).takeIf { i > BigInteger.ZERO } }.forEach {
        ans *= it
    }
    return ans
}

注釈

あくまで「のように」であって、sequence関連は奥深く、多くの使い道があります。
qiita.com

【JDBC】SimpleJdbcInsertで複数の要素を一気にInsertする

BeanPropertySqlParameterSourceの配列を作ってSimpleJdbcInsert#executeBatchに入れるとできます。

//配列を作る関数
private BeanPropertySqlParameterSource[] makeParamArray(List entities){
    BeanPropertySqlParameterSource[] sources = new BeanPropertySqlParameterSource[entities.size()];
    for(int i = 0; i < entities.size(); i++){
        sources[i] = new BeanPropertySqlParameterSource(entities.get(i));
    }
    return sources;
}

//インサート例
public void insertテーブル名(List<[Entity]> entities) {
    SimpleJdbcInsert jdbcInsert = new SimpleJdbcInsert(jdbcTemplate).withTableName("[テーブル名]");
    jdbcInsert.executeBatch(makeParamArray(entities));
}

【JDBC】SimpleJdbcInsertでDEFAULTを設定したEnumのInsert時にcannot be nullと言われる状況への対処

wrongwrong163377.hatenablog.com
前回の記事がトラブっていた原因が判明したので書き直します。

原因

NOT NULLEnumカラムに対して値を指定しない場合、com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Column '[カラム名]' cannot be nullになります。

詳細

テーブルの内指定していないカラムに関する値は全てNULLで埋められてしまうため、「NULLをInsertしようとする→NULLは設定できないのでエラー」となります。

前回の記事で書いた↑の内容は正しいです。
ただし、Enum以外(自分が見つけられなかったので他有るかは不明です)の場合勝手にデフォルトを使ってリトライするようで、問題は起きません。

対処

DEFAULTを使いたいカラム名usingGeneratedKeyColumnsに設定すればOKです。

SimpleJdbcInsert jdbcInsert =
    new SimpleJdbcInsert(jdbcTemplate)
            .withTableName("[テーブル名]")
            .usingGeneratedKeyColumns("[DEFAULTを使うカラム名]");

前回のやり方をしてしまうとBeanPropertySqlParameterSourceを使えないので、こちらのやり方が演繹的です。

【Arrow】トランポリンを使う【Kotlin】

wrongwrong163377.hatenablog.com
qiita.com

続きです。

Arrowとは

Kotlinで関数型プログラミングをするためのライブラリです。
以下の記事で知りました。
techblog.picappinc.jp

トランポリンの実装と使い方

GitHubに実装と使い方(相互再帰で奇偶判定)が上がっています。
github.com
github.com

導入方法

トランポリンのみを使う場合は、ideaのライブラリ追加→from mavenから「io.arrow-kt:arrow-free:0.8.1*1」を追加すれば導入完了です。
f:id:wrongwrongwrongwrong163377:20181224172512p:plain
f:id:wrongwrongwrongwrong163377:20181224172437p:plain

実装してみた

簡単な階乗を実装してみました。

import arrow.free.Trampoline
import arrow.free.TrampolineF
import arrow.free.map
import arrow.free.runT
import java.math.BigInteger

fun fact(n: Int): TrampolineF<BigInteger> = when(n < 2){
    true -> Trampoline.done(BigInteger.ONE)
    else -> Trampoline.defer { fact(n-1) }.map { x -> x * n.toBigInteger() }
}

fun main(){
    val x = fact(10000).runT()
    println(x)
}
実装の続き

トランポリン含め幾つかの末尾再帰アルゴリズムを実装したものをGitHubに上げています。
github.com

*1:執筆時点の最新版

【JDBC】SimpleJdbcInsertでDEFAULTを設定したカラムへのInsert時にcannot be nullと言われる状況への対処

この記事に書いたやり方でも動きますが、より演繹的な方法が有ったのでこちらもどうぞ。

wrongwrong163377.hatenablog.com

問題

SimpleJdbcInsertでInsert時に、DDLでNOT NULLとDEFAULTを設定したカラムについてMapSqlParameterSourceに値を指定しなかった場合、com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Column '[カラム名]' cannot be nullと言われます。

原因

テーブルの内指定していないカラムに関する値は全てNULLで埋められてしまうため、「NULLをInsertしようとする→NULLは設定できないのでエラー」となります。

対策

以下のように、usingColumnsを使って使うカラム名を初期化時に指定することで、問題が発生しなくなります。

SimpleJdbcInsert jdbcInsert =
    new SimpleJdbcInsert(jdbcTemplate)
            .withTableName("[テーブル名]")
            .usingColumns("[使うカラム名1], [使うカラム名2], [使うカラム名3], ...);

【プログラミング】sealed classでトランポリンを実装する【Kotlin】

2018/12/22追記

ArrowというKotlinでの関数型プログラミングを支援するライブラリにトランポリンの実装が有りましたので、Arrowを使った方がいいです。
wrongwrong163377.hatenablog.com

本文

sealed classを使ってトランポリン(Trampoline)を実装します。

sealed classとは

超簡単に言うと便利になった列挙型(enum)です。
どう便利になったかというと、sealed classを継承したクラスごとに状態や振る舞いを記述できるようになっています。

詳しい解説は以下の記事が参考になりました。
qiita.com

トランポリンとは

再帰を末尾再帰に書き直せるようにするデータ構造です。
トランポリンを用いることで、相互再帰のような、通常では末尾再帰に書き直せないコードも末尾再帰として書き表すことができます。

トランポリンそのものの動きを書くと長くなりそうだったので、sealed classを使ってトランポリンを実装することだけを書きます。
動作の仕組みについては以下にまとめました。
qiita.com

末尾再帰について

末尾再帰については以下から続けて幾つか書いているので、よければ参考にしてください。
wrongwrong163377.hatenablog.com

sealed class部分を実装する

トランポリンでは「処理継続」と「処理終了」の2状態、加えてそれぞれの状態の値が必要となります。
「処理継続」の場合は処理として関数が、処理終了の場合は結果として定数が必要となります。

というわけで、sealed classの実装は以下となります。
処理の継続をMoreが、処理の終了をDoneが表しており、値取り出し用にabstractとしてgetValue()を実装しています。

//トランポリン
sealed class Trampoline<T>{
    //処理の継続
    class More<T>(val calc: () -> Trampoline<T>): Trampoline<T>(){
        override fun getValue(): T {
            return calc().getValue()
        }
    }
    //処理の終了
    class Done<T>(private val value: T): Trampoline<T>() {
        override fun getValue(): T {
            return value
        }
    }
    //値取り出し用のインターフェース
    abstract fun getValue(): T
}
トランポリン全体の実装

トランポリンを用いて末尾再帰を行うための関数2つを書きました。
本当はトランポリンのシールドクラス内にstatic関数として実装したかったのですが、Kotlin的にはパッケージレベル関数として記述するのが正しいようなので、外に出して実装しています*1

前述したとおり、これの動きの解説は長くなるので別の機会にやるつもりです。

package trampoline

//トランポリン
sealed class Trampoline<T>{
    //処理の継続
    class More<T>(val calc: () -> Trampoline<T>): Trampoline<T>(){
        override fun getValue(): T {
            return calc().getValue()
        }
    }
    //処理の終了
    class Done<T>(private val value: T): Trampoline<T>() {
        override fun getValue(): T {
            return value
        }
    }
    //値取り出し用のインターフェース
    abstract fun getValue(): T
}

//トランポリンのランナー
fun <T> run(func: () -> Trampoline<T>): T = run(func()).let {
    when (it) {
        is Trampoline.Done -> it.getValue()
        else -> throw Exception()
    }
}
//再帰処理本体
private tailrec fun <T> run(trampoline: Trampoline<T>): Trampoline<T> = when (trampoline) {
    is Trampoline.Done -> trampoline
    is Trampoline.More -> run(trampoline.calc())
}

参考にさせて頂いたコード

実装にあたってはこちらのコードを参考にさせて頂きました。
gist.github.com

*1:参考にさせて頂いたコードでは、companion objectを用いてシールドクラス内に実装しています。