LoginSignup
4
4

More than 5 years have passed since last update.

多重代入の右辺にオブジェクト(≠配列、リスト)を持ってきたときの各言語の挙動

Posted at

相変わらずタイトルが微妙なalucky0707です。よろしくお願いします。

Rubyの場合

さて、この前Rubyでプログラミングをしていたとき、このような風なコードを書きました。

m, x, y, z = src.match /(\d+)\+(\d+)=(\d+)/

足し算の式を正規表現を用いて解析する、という内容です。 String#match(RegExp) の結果は 配列のようなオブジェクト だったので、このように分割代入すれば上手くいくと思いました。
しかし、このコードは意図した通りに動きません。
変数 m にマッチの結果が全て代入され、x, y, znil となります。

このコードを書いたときは、咄嗟に上手い解決策が思い付かず、次のように書いて誤魔化しました。

m = src.match /(\d+)\+(\d+)=(\d+)/
a = m[1]; b = m[2]; c = m[3];

なにが悲しくてRubyでこんなにダサいコードを書かなくちゃならないんだ……。

このとき分かったことは、Rubyでは 多重代入の右辺が配列ではない場合、左辺の最初の変数に値が代入される ということでした。

その後、Rubyで配列のようなオブジェクトを多重代入したい場合は、次のように書けばいいことに気づきました。

#優先順位の関係でメソッド呼び出しの括弧が必要
m, x, y, z = *src.match(/(\d+)\+(\d+)=(\d+)/)

いわゆるsplat展開をしています。
というのも、Rubyではsplat展開の際に暗黙的に to_a を呼び、配列に変換します。それを利用して、マッチの結果を配列に変換して、多重代入できるようにしているわけです。

この方法は、 to_a が適切に実装されているクラスなら使えるので、かなり便利なんじゃないかと思います。

Pythonの場合

タイトルに「各言語の」なんてつけてしまったので、Pythonについても調べてみました。

Pythonでは、多重代入の際 listtuple な気もする。Pythonは詳しくないから分からない)に変換して代入しようとします。そのため、暗黙的に __iter__ が呼ばれるようです。

具体的に示してみます。(Python3.3で書いたコードなのでPython2.xでは動くか分かりません)

class ConstList:
  def __init__(self, _list):
    self._list = list(_list)
  def __iter__(self):
    return iter(self._list)

obj = ConstList([1, 2, 3])
a, b, c = obj
print("%d %d %d" % (a, b, c))

色々無駄の多いコードですが、コンストラクタで配列を受け取り、その配列を __iter__ 内でイテレーターに変換して返しています。

いっそイテレーターから直接代入するような仕様になってれば面白かったんですけどね…。

JavaScriptの場合

JavaScriptで多重代入?そんなのあったっけ?
JavaScript 1.7?よく聞こえません。

CoffeeScriptの場合

CoffeScriptはベースがJavaScriptなので、個人的には物凄く期待通りに動きます。

こんな感じ。

#配列ライクなオブジェクト
obj =
  1: 100
  2: 200
  3: 300
[a, b, c] = obj
console.log '%d %d %d', a, b, c

src = '123+456=789'
[m, x, y, z] = src.match /(\d+)\+(\d+)=(\d+)/
console.log (+x)+(+y)is(+z)

'[ ]' を付けなくてはいけないのが難点な気もしますが、僕は明示的で結構好きです。

また、こんな書き方もできます。

obj1 =
  hoge:
    1: 'hoge-1'
    2: 'hoge-2'
  fuga: 'fuga'

{hoge:[hoge1,hoge2],fuga} = obj1
console.log hoge1, hoge2, fuga

obj2 =
  hoge: 'hoge'
  fuga: 'fuga'

{hoge, fuga} = obj2
console.log hoge, fuga

ネストした値も取り出せるってわけです。
二つ目の例は、 require したモジュールの使うものが限られてるときなんかに使うと便利です。

さらに、話が逸れてしまいますがしまいますが、こんな書き方もできます。

addArray = ([a, b]) -> a + b

console addArray([1, 2])

仮引数にもこのパターンが指定できます。
便利なようでいまいち使いどころが思い付きません。

Ruby・Pythonに比べると、CoffeeScriptは関数型言語が注目されはじめてから開発されただけあって柔軟な表現ができる気がします。

その他、HaskellとかScalaとかだと多重代入というかパターンマッチを使って、かなり分かりやすくオブジェクトを分解することが出来るはずです。
また、RubyやPythonの多重代入は制限がそれなりにあるので注意してください。

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