14
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2018-07-15

はじめに

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

配列リストミュータブルなリスト

  • 配列はkotlin.collectionsパッケージ内に定義されていないが、forEach, map, filter, findなどは使える。
  • rubyの配列はサイズの変更、書き換えなど自由に行えるが、kotlin(というかJava)では使い分けが必要。
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
array = ["Tokyo", "Osaka", "Aichi", "Fukuoka"]
array[4] = "Hokkaido" #=> "Hokkaido"
array[0] #=> "Tokyo"

マップミュータブルなマップ

  • rubyの場合はHashに相当する。
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
hash = { a: "apple", b: "banana", c: "cherry" }
hash[:a]
hash[:g] = "grape"

レンジ

  • ..演算子を用いる
  • 終了値も含まれる。pythonは含まれないので注意!
kotlin
1..10
10 in 1..10 //=> true
ruby
1..10

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

if

kotlin
val number = 6
val evenOrOdd = if (number % 2 == 0) "even" else "odd"
val evenOrOdd = if (number % 2 == 0) {
                    "even"
                } else {
                    "odd"
                }
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
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
val score = 75
val judge = when(score) {
               in 0..49 -> "不可"
               in 50..64 -> "可"
               in 65..79 -> "良"
               in 80..100 -> "優"
               else -> "欠席"
            }
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
val name = "Kotlin"
println("Hello, ${name} World!")
ruby
name = "ruby"
puts("Hello, #{name} World!")

文字列を改行込みで複数行

kotlin
val text = """
  あいうえお
  かきくけお
  さしすせそ
""".trimIndent()
ruby
# ruby 2.3以上で <<~ が使用できる。
text = <<~EOS
  あいうえお
  かきくけこ
  さしすせそ
EOS
text #=> "あいうえお\nかきくけこ\nさしすせそ\n"

関数

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
def add(num1, num2)
  num1 + num2
end

デフォルト引数

kotlin
fun concat(str1 : String, str2 : String, separator : String = ",") : String {
    return "${str1}${separator}${str2}"
}
ruby
def concat(str1, str2, separator = ",")
  "#{str1}#{separator}#{str2}"
end

名前付き引数

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

可変長引数

kotlin
fun concat(separator : String, vararg strings : String) : String {
  return strings.joinToString(separator)
}

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

concat(",", "str1", "str2", "str3")
# => "str1,str2,str3"

関数型

  • kotlinでは、関数をオブジェクト化できる。(そのオブジェクトの型は関数型となる)
  • rubyではlambda(あるいはProc.new)やMethodクラスを用いる。
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
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
text?.length // Safe Call Operator.
ruby
text&.length # Safe Navigation Operator. 日本語では「ぼっち演算子」とも呼ばれる。 

エルビス演算子

  • anull(rubyの場合nil)ならbを返す
kotlin
a ?: b
ruby
a || b

型チェック

kotlin
text is String
ruby
text.is_a?(String)

クラス

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
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
class Person() {
    companion object {
        const val CONST = "constant value"
    }
}
class Person
  CONST = "constant value".freeze
end

クラス継承

kotlin
// 継承可能なクラスはopen修飾子をつける
open class Animal(val name : String) {
    fun sleep() = print("${name} sleeps")
}
class Cat(name : String) : Animal(name) { // 継承時には親クラスのコンストラクタも明示
    fun mew() = print("mew!")
}
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
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
# 強いて言えばStructでしょうか。。
User = Struct.new("User", :id, :name, :age)

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

無名クラス

kotlin
fun foo() {
    val textPrinter = object {
        fun printText(text : String) = print(text)
    }
    textPrinter.printText("Hello World!")
}

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

foo
# => Hello World!

シングルトン

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

Printer.printText("Hello World!")
//=> Hello World!
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
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
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
// 継承可能なクラスは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
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
"text".let { it.length } // letの場合、itで受け取ることができる
"text".let { str -> str.length } // 変数を明示することも可能。
"text".run { length } // runの場合、itは不要。
"text".run { this.length } // thisで受けることができる。
//=> 4
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
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
class User
  attr_accessor :name
end
user = User.new.tap { |u| u.name = "Jiro" }

user.name
# => Jiro

コレクションの関数

filter関数(rubyではselect)

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
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
fruits.find { it.name == "melon" }
fruits.find { fruit -> fruit.name == "melon" }
//=> Fruit(name=melon, price=400)
ruby
fruits.find { |fruit| fruit.name == "melon" }

map関数

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

maxBy関数

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

minBy関数

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

forEach関数

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

reduce関数

  • rubyにはreduceRightはない。
kotlin
listOf(1, 2, 3, 4, 5).reduce { sum, n -> sum + n } //=> 15
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
arrayOf(1, 2, 3, 4, 5).fold(100) { sum, n -> sum + n } //=> 115
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(2つの時)やTriple(3つの時)を使う。
kotlin
val (a, b) = Pair(1, "B")
val (a, b, c) = Triple(1, "B", arrayOf("c"))
ruby
a, b = [1, "B"]
a, b, c = [1, "B", ["c"]]

参考

14
10
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
14
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?