Ruby
Python
プログラミング
プログラミング初心者

ruby 2.6 が出たので触ってみて、python と比較してみた

※ この記事はちょー長いです。垂れ流しアウトプットです。そして割とニッチなネタが多いです。

※ けどやって良かった。頭の整理と手を動かす良い機会になった。


はじめに


どうも

python 歴は 5-6 年ですが、ruby は 3 日のド素人です。

ruby ってなんかputとか@とか| do |とか出てくるやつでしょ?

みたいな状況からごにょごにょと 3 時間ほど書いて、たのしい Ruby を 2 時間くらいで流し読みして、そっから 12 時間くらいでまとめました。

前から ruby が気になっていて、2.6 のリリースノートが面白そうだったので休みに一気にやってみました。

ちなみに python は新人の半ばくらいの時に vim のプラグインを作りたくて python ruby perl からひとつ選ぶってなったときに、「プログラミングなんてなーんもわからん。とりあえずなんか名前がカッコイイ。」って理由で始めました。


向き不向きがあるので、選択肢を増やしたい

ruby と python のどちらが優れている!とかやる気は毛頭なくて、「〜〜するんならこっちの方が楽そう」ってのが理解できたら良いな、と思う。

例えば僕は「何か作ろ」ってなったとき、大体こんな感じで言語を決める。

(会社の制約や集団の経験言語も考慮しているので、ひとつの例として...)

まず
ざっと
んで

大体どう作るか当たりが付いてる
自分専用 and IO 少なめ
haskell

他の人のローカルでも叩くことがある
python

思いついたクラス設計の検証をしたい
scala

gradle 関係 and 文字列やリスト操作多め
groovy

gradle 関係 and クラスや高階関数多め
java (+ javaslang + lombok)

会社の適当なインフラで web る
php

テキスト整形 and 他人がそれを再現しなくて良い
vim

クソネタ作って Lightning Talk する
js (+ github-pages)

悩みながらになりそう...
チャレンジする気分
haskell

ちょっと大きい
scala

さっさと動かしたい
python

else

python

python に落ちることが多いし、得意で好きなんだけど、どうしても気にくわないことがある!

これ!!


python

>>> lines = ['main.py', 'main.rb', 'readme']

>>> map(lambda line: 'python' if line == 'py' else 'ruby', map(lambda line: line.split('.')[1], filter(lambda line: '.' in line, lines)))
# => ['python', 'ruby']


辛い!!ネストすんの辛いわ!!別に書けるけど!!読むとき目線移動大変なんだよ!!最初に読むのここだよ!?


python

#                                                                                               v ここ!!

>>> map(lambda line: 'python' if line == 'py' else 'ruby', map(lambda line: line.split('.')[1], filter(lambda line: '.' in line, lines)))
# ^ んで次ここ!!

そんなわけで変数に@とか$とかあってめんどそう、&:みたいな呪文怖い、do |x|| do | x?って思って避けてた ruby を、チェインができるのでやってみることにした。

ちなみに、自分が ruby を使う状況で使わないだろうと思ったこととかは飛ばした。


  • 継承とか

  • mix-in とか extend とか

  • 例外

  • 特異メソッドとか

  • ブロックつきメソッドの自作(欲しくなったらやる)

  • ファイルモジュール(これはいつも linux コマンドの発行でやっちゃう)

  • 日付まわり

あ、ruby は 2.6.0 で python は 2.7.14 です。(なんとなくずっと 2.7 使ってる...)

あとはひたすら垂れ流します。


文字列


出力

まずはここから。


ruby

名前
特徴
1
"1"
"foo\tbar"
[1, 2, 3]
用途

p
.inspect + 改行
1
"1"
"foo\tbar"
[1, 2, 3]
デバッグ

puts
.to_s + 改行
1
1
foo bar
1
2
3
人間に見せる

print
.to_s
1
1
foo bar
[1, 2, 3]
人間に見せる

ppはとりあえず要らないかな、って思って略。

to_s書いても自作クラスがpで使ってくれない、って思ったけどinspectなんてのがあるのか。

とりあえず開発中はpで良さげ。

haskell のprintputStrLnputStrと似てる。


python

python は print しかないので特に迷うことはない。

改行したくなければprint x,ってやるって覚えてるとちょい便利、くらい。


組み立て

よく使うので個人的にはかなり大事なポイント。


ruby

(ここではpじゃあなくてputsを使う)

変数や特殊文字が入らないなら、'"も同じで、片方を中でそのまま使える。


ruby

puts "I'm John."           # => I'm John.

puts '{"name": "John"}' # => {"name": "John"}

特殊文字や式展開は"じゃあないと無効。


ruby

name = 'John'

puts 'win path is doc\readme.md' # => win path is doc\readme.md
puts "win path is doc\readme.md" # => eadme.md is doc

puts 'my name is #{name}.' # => my name is #{name}.
puts "my name is #{name}." # => my name is John.


ってことは... json 作るのちょっとめんどい??


ruby

puts "{\"message\": \"I'm #{name}.\"}"    # => {"message": "I'm John."}


here document があるので、これなら良い??


ruby

puts <<EOS

{"message": "I'm
#{name}."} # => {"message": "I'm John."}
EOS

groovy と似てるかな。

式展開は素直に書けて良い感じ。


python

python は'"に違いはない。どっちかを外側にしてもう片方を中で使えるのも ruby と同じ。


python

print "I'm John."              # => I'm John.

print '{"name": "John"}' # => {"name": "John"}

変数展開がどちらででもできるのが好きだけど、展開場所の指定は ruby の方がすっきりしてると思う。

.formatlocals()等の細かいことは略。)


python

name = 'John'

print 'my name is %s.' % name # => my name is John.
print "my name is %s." % name # => my name is John.


ruby の'に相当するものは、r''(もしくはr"")になる。raw string の r だろな。


python

print r'win path is doc\readme.md'    # => win path is doc\readme.md

print 'win path is doc\readme.md' # => eadme.md is doc

here document は"""で囲う。1行でも可能。この場合は中で"'が使える。


python

print """{"message": "I'm %s."}""" % name # => {"message": "I'm John."}


json や linux コマンドを組み立てるときに便利。

"""は地味だけど python の好きなところのひとつ。


日本語

こりゃあ ruby の方が試すまでもなく楽だろね。


ruby


japanese.rb

puts "仕様書.doc\nmain.rb\ntest.rb"


こんなスクリプトに対してコマンドラインから叩くと、当然予想通り動く。

$ ruby japanese.rb | grep rb

main.rb
test.rb

2.0 以降は標準で utf-8 らしい。


python


japanese.py

# -*- coding: utf-8 -*-

print u'仕様書.doc\nmain.py\ntest.py'


$ python japanese.py 

仕様書.doc
main.py
test.py

問題ないと思いきや、怒られる。

$ python japanese.py | grep py

Traceback (most recent call last):
File "japanese.py", line 6, in <module>
print u'仕様書.doc\nmain.py\ntest.py'
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-2: ordinal not in range(128)

コマンドラインに出力するときと、| に繋いだりして出力するときで挙動が違う。

あと vi の中から叩いたりしても同様。


japanese.py

# -*- coding: utf-8 -*-

import sys, codecs
sys.stdout = codecs.getwriter('utf-8')(sys.stdout)

print u'仕様書.doc\nmain.py\ntest.py'


こうする。妙に大変。知ってた。


インタプリタ


型を調べる


ruby


ruby

irb(main):001:0> 'foo'.class

=> String

irb(main):004:0> 'foo'.chomp.class
=> String

irb(main):010:0> 'foo'.start_with?.class
=> FalseClass


なんだけど、どうも思ってたのとちょっと違う...?? これ評価結果に.classしてるよね...

メソッドか属性値なのか知りたくなったりしないのかな...??


python


python

>>> type('foo')

<type 'str'>

>>> type('foo'.strip)
<type 'builtin_function_or_method'>

>>> type(os.path)
<type 'module'>

>>> type(os.path.join)
<type 'function'>


とりあえず混乱したらtypeして一息付く感じ。これだけで大分手がかりになる。


属性を調べる


ruby


ruby

> class Foo

> def name()
> 'foo'
> end
> end

> foo = Foo.new()

> foo.methods
=> [:name, :instance_variable_defined?, .. .. :__send__]

> ''.methods
=> [:to_r, :encode, :encode!, :include?, :%, .. ..]


これは python と似た感覚で使えそう。ちょっと長いけど。


python


python

>>> class Foo:

... def name(self):
... return 'foo'

>>> foo = Foo()

>>> dir(foo)
['__doc__', '__module__', 'name']

>>> dir(str)
['__add__', '__class__', .... 'upper', 'zfill']


typeと合わせて使う。


help


ruby


ruby

> help String#match

(from ruby core)
------------------------------------------------------------------------
str.match(pattern) -> matchdata or nil
str.match(pattern, pos) -> matchdata or nil

------------------------------------------------------------------------

Converts pattern to a Regexp (if it isn't already one),
then invokes its match method on str. If the second parameter is
present, it specifies the position in the string to begin the search.

'hello'.match('(.)\1') #=> #<MatchData "ll" 1:"l">
'
hello'.match('(.)\1')[0] #=> "ll"
'
hello'.match(/(.)\1/)[0] #=> "ll"
'
hello'.match(/(.)\1/, 3) #=> nil
'
hello'.match('xx') #=> nil


helpでとりあえず起動してから色々探すってこともできる。


python

実は知らなくて、ruby の本読んでいて「へー、python にもあんのかな?」って思って叩いたら良いのがあったw


python

>>> help(map)

Help on built-in function map in module __builtin__:

map(...)
map(function, sequence[, sequence, ...]) -> list

Return a list of the results of applying the function to the items of
the argument sequence(s). If more than one sequence is given, the
function is called with an argument list consisting of the corresponding
item of each sequence, substituting None for missing values when not all
sequences have the same length. If the function is None, return a list of
the items of the sequence (or a list of tuples if more than one sequence).


良いことを知ったw


file


読み込み


ruby

あんま自信ない...


ruby

lines = File.read('foo.txt').split()

p lines # => ["foo", "bar", "bla bla bla", "pon"]

これ、close してないことになる?

.openしてFileオブジェクトを一度手に入れて、.readしたら.closeするか、ブロックを渡す必要があるんだよね?

ブロックを渡す方は、要はローンパターンだよね。

これで確認できるのか自信ないけど、.closeを書き換えて試してみた。


ruby

class File

def close; puts 'close!!!'; end
def write(str); STDOUT.write(str); end
end

f = File.open('foo.txt')
f.close # => close!!!

File.open('foo.txt', 'r') {} # => close!!!

lines = File.read('foo.txt') {} # =>

lines = File.read('foo.txt').split # =>

lines = File.open('foo.txt', 'r') {|f| f.read.split} # => close!!!

lines = File.open('foo.txt', &:read).split # => close!!!


ちゃんと close させるにはopen経由しないとだめなの?ならなんでFile#readがあるの?

ちょっとしたスクリプトならあんま気にしない感じなの?


python

こうとか。


python

with open('foo.txt', 'r') as f:

lines = f.read().splitlines()

with open('foo.txt', 'r') as f:
lines = map(str.strip, f)


下のは最近教えてもらった。

慣れるまでちょい変な感じがしたけど、シンプルだと思う。慣れたからかな?


モジュール

正直ここはそんなに興味ないので、ruby のrequireをちょっとだけ。


読み込み


ruby


ruby

puts 'foo'

if 1 == 1
require './japanese'
require './japanese'
end

puts 'bar'


$ ruby import.rb 

foo
仕様書.doc
main.rb
test.rb
bar

冒頭じゃあなくても書けるし、2度は読み込まれないっぽい。python と同じ。

冒頭じゃあなくても良いのは地味に好きで、例えばifに落ちた時だけrequireするとかができる。

そうすると影響が小さくしやすくて読みやすいし消しやすくて好き。(統合開発環境とか全く使ってないのでそこら辺も手作業。)

ただ、名前が競合したりするのは気にしないのかな?


imported_one.rb

def n()

'n (one)'
end

def one()
'one'
end



imported_two.rb

def n()

'n (two)'
end

def two()
'two'
end



ruby

require './imported_one'

require './imported_two'

puts n # => n (two)



python

名前が被ったり、手元のローカル変数と被るのが気になるときはメソッド単位でimportするか、モジュール名を略さない。


python

from imported_one import *

from imported_two import *

print n() # => n (two)



python

from imported_one import n, one

from imported_two import two

print n() # => n (one)



python

import imported_one

import imported_two

print imported_one.n() # => n (one)


python の方が融通が利くけど、このimport.とかfromが難しすぎる面もあるので、どっちもどっちなのかな? -> Pythonのimportについてまとめる


演算子


overload

二項演算子はどっちも大体同じ。


ruby


ruby

class Lines

attr_reader :xs

def initialize(xs)
@xs = xs
end

def +(o)
Lines.new(@xs + o.xs)
end

def -@()
Lines.new(@xs.reverse)
end

def inspect()
@xs.join(', ')
end
end

lines1 = Lines.new(['foo', 'bar'])

p lines1 + Lines.new(['pon']) # foo, bar, pon
p -lines1 # bar, foo


これは1 + 21.+(2)に見えていれば理解は簡単。scala と同じ。

Stringに対しても可能で、単項演算子も可能ってのは面白いなって思った。


python


python

class Lines:

def __init__(self, xs):
self.xs = xs

def __add__(self, o):
return Lines(self.xs + o.xs)

def __str__(self):
return ', '.join(self.xs)

print Lines(['foo', 'bar']) + Lines(['pon']) # => foo, bar, pon


似た感じ。


条件分岐

これは正直大差ないだろと思ってたけど、かなり違った。


文か式か


ruby


ruby

if 1 == 1

p 'true' # => true
else
p 'false'
end

これは python を同じだけど、こいつ値返すらしいぞ!


ruby

x = if 1 == 1

'true'
else
'false'
end

p x # => true


こいつはすげぇ!

これを


ruby

if 1 == 1

foo = 1
bar = foo + 2
foo = Foo.new(foo + bar)
else
pon = 1
foo = Foo.new(pon * 2)
end

p foo.twice()


こうできる!


ruby

p Foo.new(

if 1 == 1
foo = 1
bar = foo + 2
foo + bar
else
pon = 1
pon * 2
end
).twice() # => 8

変数大嫌いだし、ifが式なのは色々と便利で良い!

メソッドの最後に書いてもそのまま値返却にできる。式は良い。

まさかと思って適当に書いてみて良かった。scala みたいだ。


python


python

if 1 == 1:

print 'true'
else:
print 'false'

python は文なんだ、ちょっと、いや結構悲しい。


三項演算子


ruby

他の言語と同じ。


ruby

x = nil

p x.nil? ? 'x is nil' : x # => x is nil


python

特殊。他で見たことない。

英語的ってやつかしら。


python

x = None

print x if x is not None else 'x is none' # => x is none

print x, if x is not None. else, 'x is none'.みたいに一息つきながら読む感じ。

ちなみに ruby の if 修飾子と違ってelseは無くせない。


switch


ruby

ifがそうだったのできっとと思ったけど、こいつも式みたい。良い!


ruby

p case x

when 'rb'
'ruby'
when 'py'
'python'
when 'hs'
'haskell'
else
'unknown'
end
# => ruby

式になってると.collectとかにすっきり収まるはず。


ruby

xs = ['rb', 'py', 'hs']

p xs.collect {|x| case x
when 'rb'; 'ruby'
when 'py'; 'python'
when 'hs'; 'haskell'
else ; 'unknown'
end
} # => ["ruby", "python", "haskell"]


良いねー!(スタイルが ruby っぽくないのはわかってやってる。自分専用コードの時だけに留める。)

scala みたい。


python

文は大嫌いなので、式になってる scala や haskell でしか使ったことない。

まぁ python にはそもそも switch 文もないけど。

5年くらい前に使い捨ての辞書を引くって方法を自分で閃いて天才かと思ったけど、調べたら常套手段だった思い出w


python

x = 'py'

print {
'rb' : 'ruby',
'py' : 'python',
'hs' : 'haskell'
}[x].capitalize() # => Python


paiza みたいなのやるときはちょいちょい使う。

完全に余談だけど、python の boolean はただの01のエイリアスなのを知ってるとこんなことができる。


python

print ['false!', 'true!'][x.startswith('p')]        # => true!

print 'true!' if x.startswith('p') else 'false!' # => true!


三項演算子より極僅かだけど短いので、コードゴルフで使う。


優先度


ruby

andより&&が強いらしい。へー。


ruby

p false && false || true     # => true

p false and false || true # => false

上は(f && f) || tで、下はf && (f || t)に相当するから。

おもしろいけど、自分で使うかは微妙。とはいえ場面が多くて、かつ慣れたら多用すると思う。


python

python はandorのみで&&||はない。これも英語的ってやつかしら。


演算子の連結


ruby

逆に ruby はこれができない。


ruby

x = 5

p 1 < x < 10


まぁちょっと考えれば1.<(x).<(10)だけど、TrueClassが前の条件覚えた.<を持ってるわけないしね。


python


python

x = 5

print 1 < x < 10 # => True


案外ないと「んあ〜」ってなるので、できるのはうれしい。


メソッド

ここが本題。


無名関数


ruby


ruby

f = lambda{|x| x + x}

p f.call(3) # => 6


defで作ったものと違い、f.call(x)みたいに呼ぶ。もしくはf[x]

こういう時にdeflambdaを比べたいから型が知りたくなるんだけど、これだとエラーなんだよなー。

もうちょっと慣れが必要っぽい。


ruby

def f(x); x + x; end

p f.class # => error

p lambda{|x| x + 2}.class # => Proc


それは置いといて、lambdaで作った方はProcと言うらしい。2.6 のリリースノートで見たぞ。


ruby

p [1, 2, 3, 4].collect do |x|

x + x
end

そんでここのdo ~ endをブロックというらしい。置き換えられるふいんき。


ruby

lamb = lambda{|x| x + x}

p [1, 2, 3, 4].collect(lamb)


...怒られた。どうやらブロックとlambdaは別物らしい。

ruby lambda to block とかでぐぐったら出るわ出るわ。鬼門らしい?

要約すると、「ブロックはオブジェクトではない。ブロックをオブジェクトにするなら&Procオブジェクトにする。」ってことっぽい。

proclambdaの違いはわかった気がするけど、breakreturnも使う気は全く無いので今は無視。

なので、こうだ。


ruby

lamb = lambda{|x| x + x}

p [1, 2, 3, 4].collect(&lamb) # => [2, 4, 6, 8]


lambda->による略記があるみたい。


ruby

lamb = ->(x) {x + x}


こっちの方がf(x) {...}と同じ形だからわかりやすいでしょって書いてあったけど、x -> {...}の方がわかりやすいと思うw

java とか scala とか haskell とか js とか、大体こうじゃあない?


python


python

f = lambda x: x + x

print f(3) # => 6


def製とlambda製に違いはない。


python

print map(f, [1, 2, 3, 4])    # => [2, 4, 6, 8]


正直ここだけ見ると python の方がシンプル。

f = lambda x: ...みたいにラムダ式を変数束縛するのは PEP 8 では非推奨です。これはあくまで個人用なので好みでやってるだけです。)


メソッド参照

名前は不適切っぽいけど。なんて言うのかな。


ruby

シンボルをProcオブジェクトにすることで、x.f(arg):f.to_proc.call(x, arg)にできるみたい。


ruby

'foo'.include?('o')                   # => true

:include?.to_proc.call('foo', 'o') # => true

引数がひとつならこんな感じでそのままはまる。


ruby

['ruby', '', 'python'].collect(&:empty?)    # => [false, true, false]


たまに見る&:f?っていう呪文みたいなのはこいつだったんだな。

&.to_procで、:がシンボル。?はただのメソッド名の慣習。呪文をひとつ習得。

ところで、なんで.collect(の中とかじゃあないと&で怒られるんだ?


ruby

f = :empty?.to_proc    # => #<Proc:0x00007f877b0cd960(&:empty?)>

f = &:empty? # => unexpected &

その辺がまだわからん...


python

似た感じ。.to_procとか.callがないのでスッキリしてる。


python

'foo'.startswith('f')                 # => True

str.startswith('foo', 'f') # => True

map(str.strip, ['foo\n', 'bar\n']) # => ['foo', 'bar']


数年触ってて、これ先日まで知らなかったんだよね...


デフォルト引数と名前付き引数

リスト渡しとか辞書渡しは使わないので無視。必要な場面に出くわしたら調べる。


ruby


ruby

def f(a: 0, b: 0, c: 0)

p [a, b, c]
end

f(b: 3, a: 1) # => [1, 3, 0]


デフォルト引数がある引数は、名前指定で渡せる。


python


python

def f(a, b, c = 0):

print [a, b, c]

f(b = 3, a = 1) # => [1, 3, 0]


デフォルト引数がなくても、名前指定で渡せる。

これは引数が多い場合に呼ぶ側の可読性が上がったりして地味に好き。


複数引数の無名関数


ruby


ruby

p [['ruby', '2.6'], ['python', '2.7']].collect{|x| "#{x[0]}-#{x[1]}"}                   # => ["ruby-2.6", "python-2.7"]


これは多分a, b = [1, 2]みたいな代入ができるんだから、きっとこうできる。


ruby

p [['ruby', '2.6'], ['python', '2.7']].collect{|name, version| "#{lang}-#{version}"}    # => ["ruby-2.6", "python-2.7"]

f = ->((name, version)) {"#{name}-#{version}"}
p [['ruby', '2.6'], ['python', '2.7']].collect(&f) # => ["ruby-2.6", "python-2.7"]


できた。

lambdaの方は、->(name, version)だと「引数の数あってねーよ」って怒られたので適当に()をもう一つ書いたら動いた。

あくまで1つの引数を受けて、受けた方がバラすのでこうなるってことかな。haskell みたい。


python


python

f = lambda (name, version): '%(name)s-%(version)s' % locals()

print map(f, [['ruby', '2.6'], ['python', '2.7']]) # => ['ruby-2.6', 'python-2.7']


ruby と同じ感じ。


python

print ['%(name)s-%(version)s' % locals() for name, version in ['ruby', '2.6'], ['python', '2.7']]    # => ['ruby-2.6', 'python-2.7']


内包表記のforの部分は,でバラして受けられるのでよく使う。


カリー化


ruby


ruby

f = ->(name, version) {"#{name}-#{version}"}

python = f.curry.call('python')

p ['2.7', '3.3'].collect(&python) # => ["python-2.7", "python-3.3"]


できるんだ!良さげ!

ただ、これが怒られるのがよくわからない。


ruby

:+.to_proc.curry('result: ').call('Ruby-2.7')    # => 'result: '.+('Ruby-2.7') だから、result: Ruby-2.7 になると思った

:+.to_proc.call('result: ', 'Ruby-2.7') # => これは result: Ruby-2.7 になるのになぁ


haskell の("result:" ++)みたいに演算子の部分適用できたらすげー便利だと思うのに...


python

できないんだよなー、残念。


関数合成

これが「良いな!」ってなって ruby を始めた。


ruby


ruby

join     = ->((name, version)) { "#{name}-#{version}" }

cap = ->(s) { s.capitalize }
surround = ->(c, s) { "#{c} #{s} #{c}" }
pipe = ->(s) { surround.call('|', s) }

f = join >> cap >> pipe

p f.call(['ruby', '2.6']) # => | Ruby-2.6 |

p [['ruby', '2.6'], ['python', '2.7']].collect(&f) # => ["| Ruby-2.6 |", "| Python-2.7 |"]


>><<、良いなぁ!

メソッド参照とカリー化も出来るってことは...


ruby

p [['ruby', '2.6'], ['python', '2.7']].collect(&join >> :capitalize.to_proc >> surround.curry['|'])    # => ["| Ruby-2.6 |", "| Python-2.7 |"]


できた!慣れてきたー!

&(f >> g)にしないで良いって事は、演算子の優先順位は&の方が弱いってこと?


python

自分で合成書けば...


python

join     = lambda (name, version): '%(name)s-%(version)s' % locals()

cap = lambda s : s.capitalize()
surround = lambda c, s : '%(c)s %(s)s %(c)s' % locals()
pipe = lambda s : surround('|', s)

comp3 = lambda f1, f2, f3: lambda x: f3(f2(f1(x)))

f = comp3(join, cap, pipe)

print f(['ruby', '2.6']) # => | Ruby-2.6 |

print map(f, [['ruby', '2.6'], ['python', '2.7']]) # => ["| Ruby-2.6 |", "| Python-2.7 |"]


あんまり恩恵感じないかな...


リスト


連結

大半をメソッドのところでやっちゃったけど。

こいつが前置きにあった一番のポイント。


ruby


ruby

xs = ['ruby', 'python', 'php', 'js']

f = lambda{|x| x % 2 == 0 ? 'even' : 'odd'}

p xs
.select{|x| x.start_with?('p')}
.collect(&:length)
.collect(&f)
.join(', ') # => even, odd


んー、良い!

改行できるのがとても良い!

.joinArrayのメソッドなので、最後に書けるのがとても良い!

目線が左上から右下に素直に行くのは、とっても大事!

ちなみに関数合成ができたので、複数引数の片方しか使わない場合は、これを


ruby

is_p = proc{|x| x.start_with?('p')}

xs = ['foo.ruby', 'bar.python', 'pon.php', 'kaz.js']

p xs
.collect{|x| x.split('.')}
.select{|name, ext| is_p.call(ext)}
.collect{|name, ext| name}
.join(', ') # => [bar, pon]


こんな風に書いたりできる。haskell 風。


ruby

fst = proc{|x, _| x}

snd = proc{|_, x| x}

p xs
.collect{|x| x.split('.')}
.select(&snd >> is_p)
.collect(&fst)
.join(', ') # => [bar, pon]


こういうのはメソッドが集まるほど強いので、個人的にはとても好き。


python


python

xs = ['ruby', 'python', 'php', 'js']

f = lambda x: 'even' if x % 2 == 0 else 'odd'

print ', '.join(map(f, map(len, filter(lambda x: x.startswith('p'), xs))))

print ', '.join(
map(
f,
map(
len,
filter(
lambda x: x.startswith('p'),
xs
)
)
)
)


辛い...

これくらいなら内包表記で書くかなー...


python

print ', '.join(

[f(len(x)) for x in xs if x.startswith('p')]
) # => even, odd

慣れてるのでそんなに読み書きは苦ではないけど...

xsを探す -> if探す -> [の左端から読む -> 上の行のjoin読む。ってなるので、目線がすんげーことになる。とても残念。

ところで、scala とか java とかもやったけど、sep.join(xs)の形って珍しいよね。普通はxs.join(sep)だよなぁ。

なんでこうなってんだろ。

listが処理を持てないのかな?。だからlenxs.len()じゃあなくてlen(xs)だしmap.mapじゃあなくてmap()なのかな?dict.get()とか.items()とかあるのにな。


スライス

地味だけど重宝する。

だいたい python と同じだった。良い感じ。


ruby


ruby

xs = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

p xs.slice(7..-1) # => [7, 8, 9]
p xs.slice(7..) # => [7, 8, 9] -1 の省略は 2.6 から
p xs.slice(0..0) # => [0]
p xs[-1] # => 9
p xs.slice(5..-2) # => [5, 6, 7, 8]
p xs.slice(5...-1) # => [5, 6, 7, 8]
p xs.slice((1..-1).step(2)) # => error


なんだよrangeなら良いんだろーと思ったけど、rangeじゃあなかった。そっかー。


irb

> (1..2).class

=> Range

> (1..2).step(2).class
=> Enumerator::ArithmeticSequence

> ((1..2) % 2).class # % は 2.6 から
=> Enumerator::ArithmeticSequence



python

[::]の意味を知っていればすっきり読み書きできる。([start:end:step]

ちょっと含む範囲が違う。step も有効で、応用で逆順がサッとできる。

python の好きなところのひとつ。


python

xs = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

print xs[7:] # => [7, 8, 9]
print xs[:1] # => [0]
print xs[-1] # => 9
print xs[5:-2] # => [5, 6, 7]
print xs[5:-2:2] # => [5, 7]
print xs[::-1] # => [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]


ちなみにxs[:1]は案外便利で、リストの先頭要素がリストのまま欲しいときに重宝する。

1000 くらいの長さのリストを試しに一周だけ動かすときとかに便利。


python

print [f3(f2(x)) for x in xs if f1(x)]        # これを試しに1回だけ動かすのに

print f3(f2(xs[0])) if f1(xs[0]) else None # がっつり書き直さなくても

print [f3(f2(x)) for x in xs[:1] if f1(x)] # [:1] だけ付けちゃう


ruby と python どちらも[]に対してやったら[]が返ってくるので安全。

稀に便利なちょいテク。


内包表記

もうちょくちょく出してるけど。


ruby

ないっぽい。

まぁ.collectとかが改行できるし、良いかな。


python

下手にforで一時変数に足し込むのは変数が散らかるので大嫌い。

mapとの使い分けは状況次第で簡潔な方ってかんじ。

mapfilterが1回ずつ出る場合はまず内包表記でやる。(ネストするから)

mapfilterがどちらか1回の場合はlambdaってタイプしないといけないかどうかとかで決めてるなぁ。


python

xs = ['python', 'ruby']

print map(len, xs) # => [6, 4]
print [len(x) for x in xs] # => [6, 4]

print map(lambda s: 'lang is %s.' % s, xs) # => ['lang is python.', 'lang is ruby.']
print ['lang is %s.' % x for x in xs] # => ['lang is python.', 'lang is ruby.']


微妙にどっちかの方がすっきりしてるw

あと新人時代に冒頭しか読めなかったエキ py に性能の話があったけど、ruby が主軸なので割愛。


flat_map

1 : *構造が入れ子になってたりする時に使う。作るものの特性上、結構使うので気になるところ。


ruby


ruby

xs = ['main.rb', 'readme.md', 'test.rb']

read_rb = ->(path) {path.end_with?('rb') ? ["this is #{path}", 'bla bla bla...'] : []}

p xs.map(&read_rb) # => [["this is main.rb", "bla bla bla..."], [], ["this is test.rb", "bla bla bla..."]]


read_rbは引数で受けたパスのファイルを open, read して、中の行をリストで返すような処理。(だと思って。)

.mapでやると[[]]になっちゃう。

(例として便宜上readme.mdをスキップしたけど、空ファイルだったりする場合も同様に[]になる。そもそも.md[]にしたいなら先に.selectするけど、これは例なので。)


ruby

p xs.flat_map(&read_rb)    # => ["this is main.rb", "bla bla bla...", "this is test.rb", "bla bla bla..."]


.flat_mapあるんだね。良い。


python

flatten的なのがないので、内包表記でfor forしたり、空リストに対して畳み込んだりする。


python

xs = ['main.py', 'readme.md', 'test.py']

read_py = lambda path: ['this is %s' % path, 'bla bla bla...'] if path.endswith('py') else []

def flat_map(f, xs):
return sum(map(f, xs), [])

print flat_map(read_py, xs) # => ['this is main.py', 'bla bla bla...', 'this is test.py', 'bla bla bla...', 'this is readme.md', 'bla bla bla...']



他、気になってること

個人的によく書くこととかを触っておきたい。


コマンドライン引数


ruby

特にrequireは不要。


ruby

p ARGV

# => ruby ./command.rb 1 3
#
# ["1", "3"]


グローバル変数だから$使うのかな?って思ってたけど、これは定数?


python


python

import sys

print sys.argv

# => python ./command.py 1 3
#
# ['./command.py', '1', '3']



コマンドライン引数パーサ

オレオレ自作コマンドを良く作るので、良く触る。


ruby

OptionParserっていう手の込んだやつと、手っ取り早くARGVを改造するgetoptsがあるみたい。


ruby

#!/usr/local/bin/ruby

require 'optparse'

params = ARGV.getopts('', 'tension:', 'lang:en')

puts "#{params['lang'] == 'en' ? 'foo' : 'ふー'}#{'!' * params['tension'].to_i}"

# => $ foo -h
# Usage: foo [options]
# --tension=VAL
# --lang=en
#
#
# $ foo --tension 4
# foo!!!!
#
#
# $ foo --tension 4 --lang ja
# ふー!!!!


すっきりしてて良い感じ。

ところで、どうして-hが生成されるの?

.getoptsARGVを破壊しているの?それとも.getoptsに渡した情報がどっかに渡ってるのかな?

get しかしてないのにちょっと気持ち悪いw


python

3 系だともっとイケてるのかもしれないけど、ずっとこんな感じでやってる。

これは適当だけど、排他オプションとかもちゃんとできるので良い。


python

#!/usr/bin/python

# -*- coding: utf-8 -*-

import os
import argparse

parser = argparse.ArgumentParser(description = 'say foo!')
parser.add_argument('tension', type = int, help = 'number of !')
parser.add_argument('-l', '--lang', default = 'en', type = str, action = 'store', help = 'language')
args = parser.parse_args()

print '%s%s' % ('foo' if args.lang == 'en' else u'ふー', '!' * args.tension)

# => $ foo -h
# usage: foo [-h] [-l LANG] tension
#
# say foo!
#
# positional arguments:
# tension number of !
#
# optional arguments:
# -h, --help show this help message and exit
# -l LANG, --lang LANG language
#
#
# $ foo 4
# foo!!!!
#
#
# $ foo 4 --lang ja
# ふー!!!!



アノテーション


ruby

検索してみたけど、そもそも@fooみたいなのないんだ。

TODO等の特定のコメントを、アノテーションコメントと言うみたい。

rails はそれをかき集めるコマンドがあるからセットでひっかかるのかな。


python

python ではデコレータと言う。

java とかのとは違う感じで、本来の処理の前後に任意の処理を被せられる。


python

def file_testing(f):

def w(path):
print 'mkdir %s if not exists\n' % path.split('/')[0] # => 書き込み先のディレクトリがなければ作る

f(path) # => 元々呼ばれた処理を呼ぶ

print '\nremove %s' % path # => 作ったファイルを消しておく
return w

def sut(path): # 本当にやりたいこと
print 'write to %s' % path

@file_testing
def sut_test(path): # sut のテスト
sut(path)
result = sut が作ったファイルを読む
assert len(result) == 2 とかする

sut_test('out/foo.txt')

# => mkdir out if not exists
#
# write to out/foo.txt
#
# remove out/foo.txt


理解できると案外さっと作れるので、メソッド本体を汚さずにゴミ掃除をしたり実行時間を計ったりできる。(詳細は割愛。)

多用はしないけど重宝する。python の好きなところのひとつ。


os コマンド実行

これもよく使う。

ファイルモジュールで再帰的にファイル探すのとか面倒なのでfind | grep使っちゃったり、ライブラリ経由して mysql 操作するのが面倒だからmysql -eで適当に叩いちゃったりする。

http ライブラリ使うのも面倒なのでcurlしちゃうし、git のオレオレラッパー作るときも、文字列でコマンド組み立てて叩いちゃう。(自分専用は、ね。)


ruby


ruby

res = `ls /tmp`.split()

p res # => ["a.md", "a.py", "a.txt", "b.txt"]


すっきりしてて良いな!

groovy みたい。

一応、同期処理かも確認しておこう。


ruby

puts "foo"

res = `sleep 2s; ls /tmp`.split()
p res
puts "bar"

ちゃんとp resが終わってから bar が出た。良かった。(groovy でよくハマるw)


python

手っ取り早いのはos.systemだけど、戻り値が受け取れない。


python

import os

res = os.system('ls /tmp')
print res # => 0

# => python command.py
# a.md
# a.py
# a.txt
# b.txt


いつもは素直にリストになってくれるcommands.getoutputを使ってる。


python

import commands

res = commands.getoutput('ls /tmp').split()

print res # => ['a.md', 'a.py', 'a.txt', 'b.txt']



ポリシー・思想


ruby

当然まだ全然わからないけど、ちょっと調べてわかったことだけ...

ストレスなく楽しむってのが最も重視されているらしい。

あとどうもドキュメントが後追いな感じ?なの?


「Matz(まつもと) が Python に満足していれば Ruby は生まれなかったであろう」と公式のリファレンスの用語集で言及されている。


へーww

でも正直なところ、思ってたよりずっと触りやすくて良いなーって思った。

純粋な oop 言語が良かったらしい。確かに python のlenとかを見ると、後付け感がすごい。

ruby は触っていて統一感がある感じがした。

知ってる言語の中だと(あんまり詳しくないけど)scala に一番似てる気がした。ド素人意見。

あと python とは逆に、同じ事をやる方法が多い印象もある。

procの呼び方がf.call(x) or f[x] of f.(x)」とか「.map or .collect」とか「-> or lambda」とか「proc of Proc.new」みたいな、別名が多くてどっちに慣れると良いんだろ?みたいなノイズも感じた。


python

the zen of python ってのがある。

あと印象深いのは、「同じ事をやるなら同じコードになる方が良い」とか「英語的」とかそんなフレーズ。

シンプルで読みやすくて、似るコードが美徳っぽい。

なので「ifがあるからswitchはない」とか「i += 1があるからi++がない」とか「&&じゃあなくてand」とか「x if x is not None else...みたいな記述になる」とかなんだろな。

mapか内包表記か」とか「いやいやimportムズいしfromとかどうすんだよ」とかも感じないわけではないけど、この考え方は好き。

(昔聞いた話で曖昧だけど、逆に perl は「同じ事をやるいろんなコードがあって良い」的な感じだった気がする。柔軟さやテクさが美徳的な。)

あった、これか


やり方はいろいろある (There's More Than One Way To Do It; TMTOWTDI)



おわりに

思ってたよりずっと満足できた。ruby にもまとめにも。集中も続いたのでガッとやってポイッてまとめて放り投げられて良かった。

あとは経験あるのみ。しばらくは ruby 書いてみようかなー。