Rubyスクリプトの書きかたについて教えてください。


1| numbers = [1 ,2, 3, 4, nil, 5, nil, 7]
2| numbers.each do |number|
3|  puts number unless number == nil
4| end

よく上のようなプログラムを書くのですが、
3行目のような「ある条件のときだけ表示をする」というケースで
変数が二回出てくるのがなんか気持ち悪いです。

もっとスッキリ書く方法はありますでしょうか?

「Array#compactをする」のような回答を望んでいるわけではなく、
3行目をスッキリ書くやりかたが知りたいです。

よろしくお願いします。

回答の条件
  • 1人2回まで
  • 登録:
  • 終了:2008/08/01 01:27:12
※ 有料アンケート・ポイント付き質問機能は2023年2月28日に終了しました。

ベストアンサー

id:kuro-yo No.3

回答回数171ベストアンサー獲得回数29

ポイント40pt

おっしゃりたい事は、たぶん、

  1. 与えられたパラメータを適当な条件でテストし、
  2. パスしたらそのパラメータを引き続くブロックに渡す

ような書き方はできないか?という事ですね。

組み込みの関数や制御構造にはそのようなものはないようです。

それでも、もしあえてやるとすると、自分で定義する事になると思います。

例えば、putsに特化するなら、

def putsnn(n)
 puts n if n
end

numbers = [1, 2, 3, 4, nil, 5, nil, 7]
numbers.each do |number|
  putsnn number
end

任意のブロックやメソッドに対応するなら、

def donn(n)
 yield n if n
end

numbers = [1, 2, 3, 4, nil, 5, nil, 7]
numbers.each do |number|
  donn number,&method("puts")
end

後者の場合、たまたまputsはKernelモジュールのメソッドで、奇麗な形には書けないと思うのですが、生憎、私もまだRuby初心者ですので、本当はもっといい書き方があるかもしれません。

その他の回答5件)

id:rubikitch No.1

回答回数120ベストアンサー獲得回数22

ポイント20pt

これが一番すっきりかつ明瞭だと思います。

numbers = [1 ,2, 3, 4, nil, 5, nil, 7]
numbers.each do |number|
  puts number if number
end
# >> 1
# >> 2
# >> 3
# >> 4
# >> 5
# >> 7
id:garyo No.2

回答回数1782ベストアンサー獲得回数96

ポイント20pt
numbers = [1 ,2, 3, 4, nil, 5, nil, 7]

numbers.map{|x|puts x if x}

これでどうでしょうか

あるいは先に配列から条件にあうものを消しておくとか

numbers = [1 ,2, 3, 4, nil, 5, nil, 7]
numbers.delete(nil)
numbers.each do |number|
	puts number
end
id:kuro-yo No.3

回答回数171ベストアンサー獲得回数29ここでベストアンサー

ポイント40pt

おっしゃりたい事は、たぶん、

  1. 与えられたパラメータを適当な条件でテストし、
  2. パスしたらそのパラメータを引き続くブロックに渡す

ような書き方はできないか?という事ですね。

組み込みの関数や制御構造にはそのようなものはないようです。

それでも、もしあえてやるとすると、自分で定義する事になると思います。

例えば、putsに特化するなら、

def putsnn(n)
 puts n if n
end

numbers = [1, 2, 3, 4, nil, 5, nil, 7]
numbers.each do |number|
  putsnn number
end

任意のブロックやメソッドに対応するなら、

def donn(n)
 yield n if n
end

numbers = [1, 2, 3, 4, nil, 5, nil, 7]
numbers.each do |number|
  donn number,&method("puts")
end

後者の場合、たまたまputsはKernelモジュールのメソッドで、奇麗な形には書けないと思うのですが、生憎、私もまだRuby初心者ですので、本当はもっといい書き方があるかもしれません。

id:garyo No.4

回答回数1782ベストアンサー獲得回数96

ポイント20pt

再回答ですが、これが何かすっきりした感じです。

numbers = [1 ,2, 3, 4, nil, 5, nil, 7]

puts numbers.reject{|x|x==nil}
id:poch-7003 No.5

回答回数43ベストアンサー獲得回数8

ポイント20pt

Rubyらしくは・・・

#こんなのダメだと怒られそうですが

numbers = [1 ,2, 3, 4, nil, 5, nil, 7]
numbers.each do |number|
  puts(number||next)
end
id:hnagoya No.6

回答回数26ベストアンサー獲得回数3

ポイント20pt

「ある条件のときだけ表示をする」

「ある条件を満たす要素の配列を生成して」「その配列の全要素を表示する」

と書き換えれば Array#select が標準で用意されていますから

[1, 2, 3, 4, nil, 5, nil, 7].select{|x| x != nil}.each{|x| puts x}

と(条件の如何に関わらず)書けると思います。

もっとも、このパターンで「条件」が「要素がnilでない」ケースが Array#compact ですから

ご質問の趣旨に沿う回答になっているかどうか、自信はないのですが。

  • id:garyo
    numbers = [1 ,2, 3, 4, nil, 5, nil, 7]

    f = lambda {|x| puts x if x}

    numbers.each do |number|
     f[number]
    end

    無理やり一回にしてみます・・・
  • id:garyo
    >|ruby|
    numbers = [1 ,2, 3, 4, nil, 5, nil, 7]
    numbers.select{|x| x != nil}.each do |number|
    puts number
    end
    ||<
    numbers.select{|x| x != nil}.each do |number|
    こういうのを一言で書けるeach_with_condition みたいなのがあるといいかも知れないですね。

    http://yowaken.dip.jp/tdiary/20070604.html
    module Enumerable を拡張するとできるかも知れないけど、
    2つのブロックを引数に取らないといけないのが難しそうです
    http://www.rubyist.net/~matz/20070512.html#p04

    http://d.hatena.ne.jp/sumii/20070521/p1#c1179813427
    rubyでは2つのブロックを渡すのはできない(?)かもです。
    #lambdaとかProcを使えばできるのだろうか???
  • id:rubikitch
    確かに僕の回答では変数が2回出てきますが、「unless number == nil」が「if number」に短縮されたことでかなりマシになったと見越しての回答です。
    現に「if 式」は「式がnilやfalseではないならば」という意味の頻出イディオムです。
  • id:sasashin
    卓袱台引っ繰り返しちっくな感想ですが、
    > 変数が二回出てくるのがなんか気持ち悪いです。
    という感覚がわからないんですよね…。

    "puts number" は「何をするか」だし、
    "unless number == nil" は「どういう時にするか」でしょ。
    本質的に違うものじゃないですか。
    だから、number が一行に二回出てきても全然気持ち悪くないんですよね、わたしは。
  • id:kuro-yo
    正しいとされるプログラマーの感覚としては、プログラムは行単位のものではなく、プログラム全体で捉えるべきものなので、
    例えば今回のArrayの例で言えば、Array#compact等を使うわけです:そのためにそのメソッドはあるのです。

    それを忌避すると、私の回答例のようにかえって美しくなくなったりわかりにくくなったりします。
    (もちろん、そういうコンストラクトが言語に備わっていればなおいいのではないか、という意見を否定するつもりもありません)

    Ruby界隈では「大クラス主義」なんていう言葉もあります。
  • id:garyo
    きっと誰かがすごい構文で解決してくれそうな予感
  • id:kuro-yo
    Arrayとputsだけに話を絞るなら、これで充分:

    numbers = [1 ,2, 3, 4, nil, 5, nil, 7]
    puts numbers.compact

    一つの行のシンプルさのためにその行だけ追求するより、
    やっぱり、プログラム全体を見た方が結果的には全ての行がシンプルになります。
  • id:garyo
    実用的には

    □ x if x
    で十分なんだけど
    これをx一回で済ます構文はあるかという問題ですね。
  • id:garyo
    numbers = [1 ,2, 3, 4, nil, 5, nil, 7]

    numbers.each do |number|
     proc{|x| puts x if x}[number]
    end


    出来た。少なくともnumberは1回しか使われていない。
  • id:kuro-yo
    sasashinさん、

    > 卓袱台引っ繰り返しちっくな感想ですが、
    > > 変数が二回出てくるのがなんか気持ち悪いです。
    > という感覚がわからないんですよね…。

    ひょっとすると、
    puts nil

    "nil"
    を出力するのが気持ち悪いという意味かもしれませんよ。
    その感覚なら、理解できなくもないです。

    つまり、「nilの時はむしろ何もしないで欲しい」という気持ちかもしれません。
    質問者のコメントや返答が全くありませんので、推測ですが。

    ただ、RubyやLL的には、むしろ何かしてくれたほうがうれしい:
    何かをさせない事は容易にできるけど、
    はじめから何もしないものに対して、新しい何かを付け加えるほうが面倒ですから。
  • id:garyo
    numbers = [1 ,2, 3, 4, nil, 5, nil, 7]

    numbers.each do |$_|
     print if $_
    end

    改行なしでいいならこれで。
  • id:kanouk
    質問者です。コメントにて失礼します。

    みなさま、回答・コメントどうもありがとうございました。
    予想以上の反応をいただいて、とても驚いています。

    さて、質問文がわかりにくくて混乱させてしまったようですが、
    質問したかったことは、
    「ifで判定しているものと、判定した結果 処理されるものが同じ場合に
    ifの判定が真だったもののみを実行する、シンプルな文法はありますか?」
    ということでした。 # まだわかりにくいですね…

    たとえば日本語でいうと「●●が正しかったら、●●を出す」ではなくて、
    「正しいときだけ●●を出す」のようなニュアンスでスッキリ書きたいというのが、
    質問の意図に近いかもしれません。 # 余計わかりにくくなったかも…

    なので、質問文で「Array#compactではなく」と書いたのは、
    チェックでNGになるものも混ぜておかないと
    「正しいときだけ●●を出す」のような回答が得られないのでは、と思ったからでした。
    実務で使うときは、たぶん躊躇なくArray#compactすると思います(笑)

    僕はRubyを使い始めてまだまだ日が浅いのですが、
    その短い経験の中でも「こんなにすっきり書けるのか!」という感動を
    幾度も味わってきたので、もしかしたらRubyなら…と根拠なく思って質問してみました。

    最後になりますが、みなさまの回答を読んで
    あらためて自分の勉強不足を痛感し、またRubyの深さも垣間見ました。
    これからも何度か質問させていただくことがあると思いますが、
    よろしくお願いします。

  • id:hnagoya
    質問者さんの意図に沿わない回答をしたのにポイント貰ってしまって恐縮なので、ヘボプログラマなりにもうちょっと
    考えてみたのですが

    「正しいときだけ●●を出す」のようなニュアンスでスッキリ書きたい

    1. 「正しい」の定義がケースバイケースで●●に依存してしまうので、●●がこれこれならば正しい、という記述が必要
    2. ●●を出す、という記述も必要
    で 1 については任意の条件を記述できるという話になれば、どうしても引数(?)に●●相当が現れざるをえないでしょ
    うし、2についても「出す」への引数に●●相当が現れる、ということで、rubyに限らずプログラムの字面上●●相当が少
    なくとも2回出てくるのはしょうがないような気がしてきました。

    出す(正しい(●●))

    みたいに書けば字面上は●●は1回しか現れませんが、「正しい」というフィルタを定義するのに仮引数が必要になります
    (正しい = lambda (x) { x!=nil} とか)ので、本質的には回避策にならないですよね(私の回答がこれ)。たまたま
    Array#compactのように組み込み機能が使えれば字面上では●●が1個だけで済むこともありますけれども、それは偶然に
    過ぎないわけですし。

  • id:garyo
    クイズみたいで面白かったです。
    yield使って書いてみました。

    numbers = [1 ,2, 3, 4, nil, 5, nil, 7]

    def foo(cond)
     yield(cond) if cond
    end


    numbers.each do |number|
     foo(number){|x|puts x}
    end

この質問への反応(ブックマークコメント)

トラックバック

「あの人に答えてほしい」「この質問はあの人が答えられそう」というときに、回答リクエストを送ってみてましょう。

これ以上回答リクエストを送信することはできません。制限について

回答リクエストを送信したユーザーはいません