kotlinでのこの書き方、rubyだとどう書くの？

# はじめに
Java（1年） -> Ruby（2年）とやってきてkotlinを勉強し始めたら、完全にruby脳になっていて配列やリストの書き方すら忘れていたので自分の備忘録的にkotlinでこう記述したとき、rubyではどう書くのかを考えてみました。
思いつき次第、随時追加される予定です。
勉強中の身ですので、間違っている箇所ありましたら、優しくご教授いただけますと幸いです。

# [配列](http://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-array/index.html)、[リスト](http://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-list/index.html)、[ミュータブルなリスト](http://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-mutable-list/index.html)
* 配列はkotlin.collectionsパッケージ内に定義されていないが、`forEach`, `map`, `filter`, `find`などは使える。
* rubyの配列はサイズの変更、書き換えなど自由に行えるが、kotlin（というかJava）では使い分けが必要。

```kotlin:kotlin
// 配列（サイズの変更不可）
val array = arrayOf("Tokyo", "Osaka", "Aichi", "Fukuoka")
array.add("Hokkaido") //=> error: unresolved reference: add
array[4] = "Hokkaido" //=> java.lang.ArrayIndexOutOfBoundsException: 4
array.get(0) //=> "Tokyo"

array.map { it.length } //=> [5, 5, 5, 7]
array.filter { it.length > 5 } //=> [Fukuoka]

// リスト（イミュータブル）
val list = listOf("Tokyo", "Osaka", "Aichi", "Fukuoka")
list.add("Hokkaido") //=> error: unresolved reference: add
list[0] //=> "Tokyo"
list.get(0) //=> "Tokyo"

// ミュータブルなリスト
val mutableList = mutableListOf("Tokyo", "Osaka", "Aichi", "Fukuoka")
mutableList.add("Hokkaido") //=> true
mutableList[0] //=> "Tokyo"
mutableList.get(0) //=> "Tokyo"
```
```ruby:ruby
array = ["Tokyo", "Osaka", "Aichi", "Fukuoka"]
array[4] = "Hokkaido" #=> "Hokkaido"
array[0] #=> "Tokyo"
```

# [マップ](http://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-map/index.html)、[ミュータブルなマップ](http://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-mutable-map/index.html)
* rubyの場合はHashに相当する。

```kotlin:kotlin
// マップ（イミュータブル）
val fruits = mapOf("a" to "apple", "b" to "banana", "c" to "cherry")
fruits["a"] //=> apple
fruits.put("g", "grape") //=> error: unresolved reference: put

// ミュータブルなマップ
val fruits = mutableMapOf("a" to "apple", "b" to "banana", "c" to "cherry")
fruits["a"] //=> apple
fruits.put("g", "grape") //=> null
```
```ruby:ruby
hash = { a: "apple", b: "banana", c: "cherry" }
hash[:a]
hash[:g] = "grape"
```

# [レンジ](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.ranges/index.html)
* `..`演算子を用いる
* 終了値も含まれる。pythonは含まれないので注意！

```kotlin:kotlin
1..10
10 in 1..10 //=> true
```
```ruby:ruby
1..10

require 'active_support/all' # ActiveSupportのコア拡張を利用
10.in? 1..10 #=> true
```
```python:python
range(1, 10)
10 in range(1, 10) #=> False
```

# `if`式
```kotlin:kotlin
val number = 6
val evenOrOdd = if (number % 2 == 0) "even" else "odd"
val evenOrOdd = if (number % 2 == 0) {
                    "even"
                } else {
                    "odd"
                }
```
```ruby:ruby
number = 6
even_or_odd = if number % 2 == 0; 'even' else 'odd' end # 非推奨
even_or_odd = if number % 2 == 0
                "even"
              else
                "odd"
              end
even_or_odd = number % 2 ? "even" : "odd" # 短い場合は三項演算子もしばしば利用される
```
```python:python
number = 6
even_or_odd = "even" if number % 2 == 0 else "odd" # pythonにおける三項演算子
if number % 2 == 0: # pythonにおけるifは文なので代入できない。
    even_or_odd = "even"
else:
    even_or_odd = "odd"
```

# `when`式
```kotlin:kotlin
val score = 75
val judge = when(score) {
               in 0..49 -> "不可"
               in 50..64 -> "可"
               in 65..79 -> "良"
               in 80..100 -> "優"
               else -> "欠席"
            }
```
```ruby:ruby
score = 75
judge = case score
        when 0..49 then "不可"
        when 50..64 then "可"
        when 65..79 then "良"
        when 80..100 then "優"
        else "欠席"
        end
```

# 文字列
## 文字列テンプレート
```kotlin:kotlin
val name = "Kotlin"
println("Hello, ${name} World!")
```
```ruby:ruby
name = "ruby"
puts("Hello, #{name} World!")
```
## 文字列を改行込みで複数行
```kotlin:kotlin
val text = """
  あいうえお
  かきくけお
  さしすせそ
""".trimIndent()
```
```ruby:ruby
# ruby 2.3以上で <<~ が使用できる。
text = <<~EOS
  あいうえお
  かきくけこ
  さしすせそ
EOS
text #=> "あいうえお\nかきくけこ\nさしすせそ\n"
```

# 関数
```kotlin:kotlin
fun add(num1 : Int, num2 : Int) : Int {
    return num1 + num2
}
fun add(num1 : Int, num2 : Int) : Int = num1 + num2 // 単一式関数
fun add(num1 : Int, num2 : Int) = num1 + num2 // 単一式関数では型推論が効き、戻り値の型が省略可
```
```ruby:ruby
def add(num1, num2)
  num1 + num2
end
```
## デフォルト引数
```kotlin:kotlin
fun concat(str1 : String, str2 : String, separator : String = ",") : String {
    return "${str1}${separator}${str2}"
}
```
```ruby:ruby
def concat(str1, str2, separator = ",")
  "#{str1}#{separator}#{str2}"
end
```

## 名前付き引数
```kotlin:kotlin
fun concat(str1 : String, str2 : String, separator : String = ",") : String {
    return "${str1}${separator}${str2}"
}
concat(str2="二つめ", str1="一つめ") // 引数名を指定して呼び出せば順序を変更できる
//=> 一つめ,二つめ
```
```ruby:ruby
# キーワード引数を用いれば同様のことが可能。
def concat(str1:, str2:, separator: ",")
  "#{str1}#{separator}#{str2}"
end
concat(str2: "二つめ", str1: "一つめ")
#=> "一つめ,二つめ"
```

## 可変長引数
```kotlin:kotlin
fun concat(separator : String, vararg strings : String) : String {
  return strings.joinToString(separator)
}

concat(",", "str1", "str2", "str3")
//=> "str1,str2,str3"
```
```ruby:ruby
def concat(separator, *args)
  args.join(separator)
end

concat(",", "str1", "str2", "str3")
#=> "str1,str2,str3"
```
## 関数型
- kotlinでは、関数をオブジェクト化できる。（そのオブジェクトの型は関数型となる）
- rubyではlambda（あるいはProc.new）やMethodクラスを用いる。
    - 個人的にはrubyのややこしいところの一つだと思う。
    - [lambdaとProc.newの違いについて](https://qiita.com/ryo-ma/items/24c46592b45775e8644d#procnew%E3%81%A8lambda)
    - [Methodクラスについて](https://docs.ruby-lang.org/ja/latest/class/Method.html)

```kotlin:kotlin
fun calc(operation : (Int, Int) -> Int) {
    val result = operation(3, 7)
    print(result)
}
val add = fun (num1 : Int, num2 : Int) = num1 + num2
calc(add) //=> 10
```
```ruby:ruby
def calc(operation)
  result = operation.call(3, 7)
  puts result
end
add = ->(num1, num2) {
  num1 + num2
}
calc(operation) #=> 10

# 既存の関数の場合は例えばMethodオブジェクト化する。
def add(num1, num2)
  num1 + num2
end
calc(Object.new.method(:add)) #=> 10
```

# 安全呼び出し演算子
```kotlin:kotlin
text?.length // Safe Call Operator.
```
```ruby:ruby
text&.length # Safe Navigation Operator. 日本語では「ぼっち演算子」とも呼ばれる。 
```

# エルビス演算子
* `a`が`null`（rubyの場合`nil`）なら`b`を返す

```kotlin:kotlin
a ?: b
```
```ruby:ruby
a || b
```

# 型チェック
```kotlin:kotlin
text is String
```
```ruby:ruby
text.is_a?(String)
```

# クラス
```kotlin:kotlin
class Person(_name : String, _age : Int) {
    val name : String = _name // nameプロパティ
    var age : Int = _age // ageプロパティ
    fun isAdult() = age >= 18
}

// コンストラクタの引数にvalやvarをつけると同時にプロパティとしても定義可能
class Person(val name : String, var age : Int) {
    fun isAdult() = age >= 18
}
```
```ruby:ruby
class Person
  attr_accessor :name, :age

  def initialize(name, age) # rubyではinitializeがコンストラクタに相当
    self.name = name
    self.age = age
  end

  def adult?
    age >= 18
  end
end
```

## クラス定数
* `companion object`の中に`const val`で定義

```kotlin:kotlin
class Person() {
    companion object {
        const val CONST = "constant value"
    }
}
```
```ruby
class Person
  CONST = "constant value".freeze
end
```

## クラス継承
```kotlin:kotlin
// 継承可能なクラスはopen修飾子をつける
open class Animal(val name : String) {
    fun sleep() = print("${name} sleeps")
}
class Cat(name : String) : Animal(name) { // 継承時には親クラスのコンストラクタも明示
    fun mew() = print("mew!")
}
```
```ruby:ruby
class Animal
  attr_accessor :name

  def initialize(name)
    self.name = name
  end
  
  def sleep
    puts "#{name} sleeps"
  end
end

class Cat < Animal
  def mew
    puts "mew!"
  end
end
```

## データクラス
```kotlin:kotlin
data class User(val id : String, val name : String, val age : Int)

val user1 = User("id1", "Jiro", 20)
val user2 = User("id1", "Jiro", 20)
user1 == user2 //=> true
```
```ruby:ruby
# 強いて言えばStructでしょうか。。
User = Struct.new("User", :id, :name, :age)

user1 = User.new("id1", "Jiro", 20)
user2 = User.new("id1", "Jiro", 20)
user1 == user2 #=> true
```

## 無名クラス
```kotlin:kotlin
fun foo() {
    val textPrinter = object {
        fun printText(text : String) = print(text)
    }
    textPrinter.printText("Hello World!")
}

foo()
//=> Hello World!
```
```ruby:ruby
def foo
  textPrinter = Class.new {
    def self.printText(text)
      puts text
    end
  }
  textPrinter.printText("Hello World!")
end

foo
#=> Hello World!
```

## シングルトン
```kotlin:kotlin
// オブジェクト宣言でクラスを定義する
object Printer {
    fun printText(text : String) = print(text)
}

Printer.printText("Hello World!")
//=> Hello World!
```
```ruby:ruby
# Singletonモジュールをincludeする
require "singleton" # Railsの場合は含まれているはず

class Printer
  include Singleton

  def print_text(text)
    puts text
  end
end

Printer.instance.print_text("Hello World!")
#=> Hello World!
```

## Companionオブジェクト
* rubyだと特異クラスを使うと似たようなことができる。（議論の余地あり）

```kotlin:kotlin
data class User(val name : String)

class MyClass() {
    companion object {
        fun newUser(name : String) = User(name)
    }
}

val user = MyClass.newUser("Jiro")
user.name //=> Jiro
```
```ruby:ruby
User = Struct.new("User", :name)

class MyClass
  def self.new_user(name)
    User.new(name)
  end
end

user = MyClass.new_user("Jiro")
user.name #=> "Jiro"
```

## メソッドのオーバーライド
```kotlin:kotlin
// 継承可能なクラスはopen修飾子をつける
open class Animal(val name : String) {
    open fun sleep() = print("${name} sleeps") // オーバーライド可能なメソッドにはopen修飾子をつける
}
class Cat(name : String) : Animal(name) { // 継承時には親クラスのコンストラクタも明示
    fun mew() = print("mew!")
    override fun sleep() = print("${name} had a deep sleep") // override修飾子をつける
}
```
```ruby:ruby
class Animal
  attr_accessor :name

  def initialize(name)
    self.name = name
  end
  
  def sleep
    puts "#{name} sleeps"
  end
end

class Cat < Animal
  def sleep
    puts "#{name} had a deep sleep"
  end

  def mew
    puts "mew!"
  end
end
```

## インターフェイス
* rubyでは該当するものがない？

# スコープ関数
## `let`関数、`run`関数
```kotlin:kotlin
"text".let { it.length } // letの場合、itで受け取ることができる
"text".let { str -> str.length } // 変数を明示することも可能。
"text".run { length } // runの場合、itは不要。
"text".run { this.length } // thisで受けることができる。
//=> 4
```
```ruby:ruby
# ruby 2.5以上
"text".yield_self { |str| str.length }
"text".yield_self(&:length)

# ruby 2.5未満
"text".tap { |str| break str.length }
```

## `apply`関数、`also`関数
 * オブジェクト生成と同時に初期設定を行いたい場合に使用

```kotlin:kotlin
class User() {
  var name : String = ""
}
val user = User().apply { name = "Jiro" } // itは不要。戻り値はレシーバ。
val user = User().apply { this.name = "Jiro" } // 明示したい時はthisを用いる
val user = User().also { user -> user.name = "Jiro" } // alsoの場合、thisはラムダの内外で変わらない。変数を明示する必要がある。

user.name
//=> Jiro
```
```ruby:ruby
class User
  attr_accessor :name
end
user = User.new.tap { |u| u.name = "Jiro" }

user.name
#=> Jiro
```

# コレクションの関数
## `filter`関数（rubyではselect）
```kotlin:kotlin
data class Fruit(val name: String, val price : Int)
val fruits = listOf(Fruit("apple", 100), Fruit("banana", 150), Fruit("melon", 400))

fruits.filter { it.price < 200 }
fruits.filter { fruit -> fruit.price < 200 }
//=> [Fruit(name=apple, price=100), Fruit(name=banana, price=150)]
```
```ruby:ruby
class Fruit
  attr_accessor :name, :price
  def initialize(name, price)
    self.name = name
    self.price = price
  end
end
fruits = [Fruit.new("apple", 100), Fruit.new("banana", 150), Fruit.new("melon", 400)]
fruits.select { |fruit| fruit.price < 200 }
```

## `find`関数
```kotlin:kotlin
fruits.find { it.name == "melon" }
fruits.find { fruit -> fruit.name == "melon" }
//=> Fruit(name=melon, price=400)
```
```ruby:ruby
fruits.find { |fruit| fruit.name == "melon" }
```

## `map`関数
```kotlin:kotlin
fruits.map { it.price * 1.08 }
fruits.map { fruit -> fruit.price * 1.08 }
//=> [108.0, 162.0, 432.0]
```
```ruby:ruby
fruits.map { |fruit| fruit.price * 1.08 }
#=> [108.0, 162.0, 432.0]
```

## `maxBy`関数
```kotlin:kotlin
fruits.maxBy { it.price }
//=> Fruit(name=melon, price=400)
```
```ruby:ruby
fruits.max_by { |fruit| fruit.price }
fruits.max_by(&:price)
```

## `minBy`関数
```kotlin:kotlin
fruits.minBy { it.price }
//=> Fruit(name=apple, price=100)
```
```ruby:ruby
fruits.min_by { |fruit| fruit.price }
fruits.min_by(&:price)
```

## `forEach`関数
```kotlin:kotlin
fruits.forEach { print("${it.name}\n") }
```
```ruby:ruby
fruits.each { |fruit| puts fruit.name }
```

## `reduce`関数
* rubyには`reduceRight`はない。

```kotlin:kotlin
listOf(1, 2, 3, 4, 5).reduce { sum, n -> sum + n } //=> 15
```
```ruby:ruby
[1, 2, 3, 4, 5].reduce { |sum, n| sum + n } #=> 15
[1, 2, 3, 4, 5].inject { |sum, n| sum + n } #=> 15
```

## `fold`関数
* 初期値を与えられる`reduce`。rubyでは`reduce`のまま引数を与える。

```kotlin:kotlin
arrayOf(1, 2, 3, 4, 5).fold(100) { sum, n -> sum + n } //=> 115
```
```ruby:ruby
[1, 2, 3, 4, 5].reduce(100) { |sum, n| sum + n } #=> 115
[1, 2, 3, 4, 5].inject(100) { |sum, n| sum + n } #=> 115
```

# 複数の変数に代入
* kotlinでは[`Pair`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-pair/index.html)（２つの時）や[`Triple`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-triple/index.html)（３つの時）を使う。

```kotlin:kotlin
val (a, b) = Pair(1, "B")
val (a, b, c) = Triple(1, "B", arrayOf("c"))
```
```ruby:ruby
a, b = [1, "B"]
a, b, c = [1, "B", ["c"]]
```

# 参考
 - [Kotlin スコープ関数 用途まとめ](https://qiita.com/ngsw_taro/items/d29e3080d9fc8a38691e)