LoginSignup
1
1

More than 1 year has passed since last update.

【狂気】Kotlinでもこんなの(cin >>)とかこういうの(cout <<)したい!!

Posted at

はじめに

この記事はinfix(中置記法)で遊んでる謎記事です。
なんだこれって思いながら読んでください。

ある日

Aさん「競プロはC++がおすすめだよー」
僕「へー!でもKotlin使いたい・・・でもAtCoderでKotlin使いづらいしな・・・」1
Aさん「C++こんな事できるんだよ!」

int a,b,c;
cin >> a >> b >> c;
cout << "あ" << "い" << "う" << endl;
/*
結果↓
あいう

*/

僕「なんて直感的な書き方なんだ!すげー!C++最高!」

でもやっぱりKotlinが好き

僕「競プロでC++使ってるけど、Kotlin信者としてはKotlin使いたいな・・・」
僕「ハッ!閃いた!Kotlinでこんなの(cin >>)とかこういうの(cout <<)できるようにすればいいんだ!」

coutを実装する

C++のcoutの機能を挙げてみます(C++初心者なので不正確)

  • cout << a << "b"で文字列型変数や文字列リテラルをつなげて出力できる。
  • endlで改行できる。
    こんな感じですかね。他にもcoutの特徴があったら教えてください。
    では、実装していきましょーー!!
cout.kt
//大文字じゃないとQiitaでエラー出るなぁ・・・
object cout {
    fun add(charSequence: CharSequence) {
        print(charSequence)
    }
    fun add(value: Any?) {
        print(value)
    }
    const val endl = "\n"
}
infix fun cout.shl(charSequence: CharSequence): cout {
    add(charSequence)
    return this
}
infix fun cout.shl(value: Any?): cout {
    add(value)
    return this
}

infixは中置記法というやつで、a until bと同じように書ける関数を定義できます。

中置記法の説明(Docsより抜粋)

infixキーワードでマークされた関数は、中置記法を使用して呼び出すこともできます。 (呼び出しのドットと括弧を省略します)
中置関数は、次の要件を満たす必要があります。

  • それらはメンバー関数または拡張関数でなければなりません。
  • パラメーターは 1 つでなければなりません。
  • このパラメーターは、可変数の引数を受け入れてはならず、デフォルト値を持っていてはなりません。

原文

coutの使用例です。

Example.kt
import cout.endl
fun main(){
    cout shl "あ" shl "a" shl endl
    cout shl "い" shl "i" shl endl
}

結果

出力
あa
いi

プロセスは終了コード 0 で終了しました

おおおお!C++っぽいですね!!

†ULTIMATE STRING†

命名はてきとーです
ローカル変数ではリフレクションが使えないので後述のcinで値の変更ができるようにCharSequenceを継承したクラスを作ります。

class UltimateString<T>(var value: T, private val mapper: (String.() -> T)? = null) : CharSequence {

    private var string: String = ""
    internal fun set(charSequence: CharSequence) {
        string = charSequence as String
        value = mapper?.let { string.it() } ?: string as T
    }

    operator fun plus(other: Any?): String {
        return string + other
    }

    override val length: Int = string.length
    override fun get(index: Int): Char {
        return string[index]
    }

    override fun subSequence(startIndex: Int, endIndex: Int): CharSequence {
        return string.subSequence(startIndex, endIndex)
    }

    override fun toString(): String {
        return string
    }

    fun get() = value
}

operator fun String.invoke(): us {
    return us(this)
}

operator fun <T> T.invoke(mapper: (String.() -> T)? = null): UltimateString<T> {
    return UltimateString(this, mapper)
}
//Ultimate Stringの略
typealias us = sus<String>
//Super Ultimate String ඞ
typealias sus<T> = UltimateString<T>

このクラスのmapperにキャスト用の関数を書くことでInt型などにも変換できるようになっています。
短い文でUltimateStringを宣言できるようにinvoke()を無理矢理使っています。

使用例
val sus = sus(0){ toInt() }
val us = 0{ toInt() }
val us = us("")

上のように書くことができます。(他にも書き方はありますが)
面倒くさいのでcompareToとかは実装してません。

cinを実装する

cinは代入しないといけないのでcoutよりも実装がめんどくさそうです。
cinの機能はこんな感じですかね

  • 入力をスペースで区切って変数に代入
  • 数値とかにキャストしてくれる
    ここで先程のUltimate Stringが活躍します。
cin.kt
import kotlin.reflect.KMutableProperty
class cin {
    private val readArray: Array<String> = readln().split(" ").toTypedArray()
    private val iterator = readArray.iterator()
    fun nextRead(): String = iterator.next()
}
infix fun <T> cin.shr(value: UltimateString<T>): cin {
    value.set(this.nextRead())
    return this
}

infix fun <T:CharSequence> cin.shr(value: KMutableProperty<T>): cin {
    value.setter.call(this.nextRead() as T)
    return this
}

ほとんどcoutと同じですが、UltimateString.set()経由で値を設定しています。
プロパティに関してはリフレクションを使って設定します。
使用例

Example2.kt
import cout.endl

var a = ""
fun main() {
    val b = 0{toInt()}
    cin() shr ::a shr b
    cout shl a shl "+" shl b shl "=" shl a.toInt() + b.get() shl endl
}

結果

出力
(入力)1 2
1+2=3

プロセスは終了コード 0 で終了しました

おおお!できましたね!!!
valで宣言してもUltimateStringだと書き換え可能ですがまあいいでしょう!!

おわりに

しょうもない記事ですが最後まで読んでくださりありがとうございます!!
良いKotlin&競プロライフを!!!

  1. AtCoderではKotlin 1.3しか使えず、readlnとかめんどくさいです。

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