Ruby 初心者スレッド Part 67
プログラミング言語 Rubyについての、初心者向けスレです。質問・要望・雑談などをどうぞ 質問するときは、OSやRubyのバージョン、エラーメッセージを書いて下さい。 Ruby on Rails については、WEBプログラミング板で 前スレ Ruby 初心者スレッド Part 66 https://mevius.5ch.net/test/read.cgi/tech/1578068134/ るりまサーチ (リファレンス検索) http://rurema.clear-code.com/ Rubyist Magazine - るびま http://jp.rubyist.net/magazine/ 逆引きRuby http://www.namaraii.com/rubytips/ Ruby コミュニティ公式 https://www.ruby-lang.org/ VIPQ2_EXTDAT: checked:vvvvv:1000:512:: EXT was configured >>72 rubyやり始めたところで、eqlがイコールって気づかなかった。短い方のイコールって良いですね!ありがとうございます。 >>73 自分もイーキューエルだて頭の中で読んでた >>74 なんでやーーー!! すみません、質問です hash生成時に、 str={"key1" = "val1"} ではエラーになる理由を教えてください。 p hash = { "a" => "b" } #=> {"a"=>"b"} 全角の{}を使うな。 文字列以外は半角英数字のみ >>77 すみません、携帯からで全角と半角を間違えてしまいました。半角として見てくださいm(_ _)m p h_1 = { "a" => "b" } #=> {"a"=>"b"} p h_2 = { "あ": "い" } #=> {:あ=>"い"} p h_3 = { one: 1, "two": "2" } #=> {:one=>1, :two=>"2"} 新しめの書き方では、2, 3 みたいに書ける : を使うと、文字列のキーがシンボルになる "two": が、:two になる one: はシンボルのまま 書いていただいた方法でないと生成できないのですね 丁寧な回答ありがとうございました AtCoderの解答見てると、rubyで巨大配列を扱うような時に激遅になりがちな気がする 配列アクセスの遅さなのかループの遅さなのかわからんが JIT があるから、1秒間で100万回ループすると、ネイティブコードにコンパイルされて、 1秒間で1,000万回ループできるようになる irbにおいて、 def test; p test; end とだけ打ち込みました。 すると、Enumerable.methods.grep(/test/) => [:test] が得られます。 何故、testメソッドが勝手にEnumerableのメソッドになるのか教えていただけないでしょうか。 環境は ruby 2.7.1p83 (2020-03-31 revision a0c7c23c9c) [x86_64-darwin18] です。 https://docs.ruby-lang.org/ja/latest/class/main.html >トップレベルで定義したメソッドは Object の private インスタンスメソッドとして定義されます。 って思ったけどこっちの環境でもprivateにならないな はて、何故でしょう ruby 2.6.6 [x86_64-linux] なら、 def test; p test; end p Enumerable.methods.grep(/test/) #=> [ ] p test が文法エラーになるのでは? test という識別子が存在しないから 0084です。 def test; p test; end ではなく、 def test; p “test”; end でした。 大変申し訳ありませんでした。 ほかのメソッドでも同じような動きを確認しています。今日は立て込んでしまっているので、追って別のメソッドの例も提示します。 >>0085さん、確認ありがとうございます。 私は初心者なので、以下の認識で正しいか、何か例外があるのか不安でお尋ねしました。認識は正しかったようですので、もう少し調べてみます。 >トップレベルで定義したメソッドは Object の private インスタンスメソッドとして定義されます。 privateかどうかをどうやって確認したの? 要再現コード ruby 2.6 => [ ] ruby 2.7 => [:test] ruby 3.0 => [ ] irb(main):001:0> def foo; p "Foo!" end => :foo irb(main):002:0> "abc".foo "Foo!" => "Foo!" irb(main):003:0> 3.14.foo "Foo!" => "Foo!" irb(main):004:0> [].foo "Foo!" => "Foo!" 90です。 パソコンを再起動して、.irb_historyをtouchで初期化したのち、 以下を実行しても同じ結果でした。 irb(main):001:0> def test; p "test"; end => :test irb(main):002:0> Enumerable.methods.grep(/test/) => [:test] irb(main):010:0> exit $ rbenv -v rbenv 1.1.2 $ rbenv versions system * 2.7.1 (set by /Users/someone/.rbenv/version) 別のメソッドも作ってみましたが、結果は同じでした。 irb(main):001:1* def mul(a,b) irb(main):002:1* a*b irb(main):003:0> end => :mul irb(main):004:0> Enumerable.methods.grep(/mul/) => [:mul] irb(main):006:0> exit $ irb irb(main):001:0> Enumerable.methods.grep(/mul/) => [] 90です。 92さんがおっしゃる通り、環境を作り直すこととします。 私の疑問を受けて、再現性について試していただいた皆様、 ご協力ありがとうございました。 irb(とpry)でトップレベルで定義するとなんかおかしい $ cat test.rb def hoge; end puts 'public: %p' % [methods.grep(/hoge/)] puts 'private: %p' % [private_methods.grep(/hoge/)] $ ruby -v test.rb ruby 2.6.1p33 (2019-01-30 revision 66950) [x64-mingw32] public: [] private: [:hoge] $ irb irb(main):001:0> load("test.rb") public: [] private: [:hoge] => true irb(main):002:0> def fuga;end => :fuga irb(main):003:0> puts 'public: %p' % [methods.grep(/fuga/)] public: [:fuga] => nil irb(main):004:0> puts 'private: %p' % [private_methods.grep(/fuga/)] private: [] => nil irb, pry は特殊なのかな? 漏れは、VSCode のCode Runner で、 選択したコード片、またはファイル全体を、右クリックメニューから実行してる これが楽 WEB+DB vol.121 Ruby 3 特集、30 ページ分 Ruby on Rails 6 の本を書いている、2人の新刊 Go言語 ハンズオン、掌田津耶乃、2021/3/6 Elixir実践ガイド、黒田努、2021/2/5 Ruby からGoか、Elixirか、どっちへ進むべきか? mruby, Rust もあるけど Ruby勉強始めたんですが ↓この部分が毎回書きづらいです コード補完とかってないんですか? a.map{|x|x+1}←の|x|の部分 >>101 Ruby 2.7 以降は、番号指定パラメータが導入されたので a.map{_1+1} プログラミング言語Crystal、初のメジャーリリースとなるバージョン1.0を公開 最近は全言語で、VSCode の拡張機能・Remote Container で、Docker を使う Windows 10 Home でも、WSL2 が出来るようになった Elixir でも、無名関数をキャプチャー演算子・& を使って、短く書ける fn a, b -> a * b end &(&1 * &2) & &1 * &2 rubyでExcelへデータを出力しようとしているんですが、 どうしてもできないことがあり質問します。 ruby -v 3.0.0p0 rubyでExcelのデータを検索するときはFindを使うと思うのですが、 日付項目(シリアル値)を検索するときはどうすればいいのでしょうか。 以下をやってみましたが、検索できませんでした。 find('3/30') ・・・Excelの表示を指定 find('4321') ・・・シリアル値を指定 >>110 使ってるライブラリが分からないと答えようがない >>111 失礼しました。 ライブラリはWin32OLEです >>110 上はできた。下はダメだった ちなみに表示が3月30日の場合、find('3/30') はいけたが find('3月30日') はダメ >>113 え、いけました?? 家に帰ったらもう一回やってみます。 教えて頂いてありがとうございます。 >>17 on ruby rails と出力されると思いきや、ruby “on ¥n rails”だって。 なんでだ? Here Documentの仕様ってどうなってんの? >>115 終端行までなんだから、その前にある改行文字は含まれてあたりまえやろ。 ヒアドキュメントの評価値とは別に #{print "ruby "} で"ruby "とprintされるクソみたいなコードだから まあなぞなぞみたいなもんでしょ クソコードとかいったら身も蓋もない Loggerで出力する内容を奪い取って、他のメソッド呼び出しにするってどう言う実装したらいいですか? 例えば全てをTest::any::log(msg)を内部的に呼び出して終わるみたいなLoggerを作るにはどうしたらいいですか? Logger.new(STDERR)だと全て標準エラーに出力ですが、これを全てTest::any::log(msg)の呼び出しで標準エラーには何も出さないloggerを作りたいのです C:/Ruby25-x64/lib/ruby/2.5.0/fileutils.rb では、 インスタンス変数に、標準エラーを代入してる @fileutils_output ||= $stderr @fileutils_output = $stderr 漏れは、それを真似て、 DryRun の時だけ、noop: true, verbose: true を付けて、 標準出力から標準エラーに切り替えるような、モジュールを作っている module MyFileUtils @fileutils_output = $stdout extend self def f( *args, **options ) # 配列・ハッシュ p @fileutils_output @fileutils_output.puts( args, options ) end module DryRun include MyFileUtils @fileutils_output = $stderr extend self def f( *args, **options ) super( *args, **options, noop: true, verbose: true ) end end end MyFileUtils.f( 1, 2, a: 8, b: 9 ) MyFileUtils::DryRun.f( 1, 2, a: 8, b: 9 ) Ruby 逆引きハンドブックには、こう書いてある Logger.new の引数には、ログファイルパスか、 write, close が定義された、IO などのオブジェクトを指定する Ruby 逆引きハンドブックには、メソッドオブジェクトも載ってる def f_a( ) :a end def f_b( ) :b end def doit( command ) m = method( command ) m.call end def doit_2( command ) __send__ command end p doit( :f_a ) #=> :a p doit_2( :f_b ) #=> :b gem installって個別のユーザー環境のみに反映することってできますか? インストールしたらそのPCを使ってる全ユーザーに反映されないようにしたいです どうしたらいいですか? 1. 環境変数のGEM_HOMEを指定する 2. --install-dirを指定してインストール 3. --user-installを指定してインストール 4. rbenvを使う どれでもいける とりあえずgem help installでヘルプを見るといい {"status"=>0, "data"=>[{"ask"=>"5370001", "bid"=>"5370000", "high"=>"5634576", "last"=>"5371160", "low"=>"5243350", "symbol"=>"BTC", "timestamp"=>"2021-04-23T03:22:36.028Z", "volume"=>"854.3707"}], "responsetime"=>"2021-04-23T03:22:37.031Z"} このハッシュ?から5370000の値を取り出したいのですが上手くいきません。助けてください >>127 jsonをパースして変数に入れてbidが持つ値を出力したいのですがnilが返ってきます >>128 やなくて、コードをはれ。 まあ、たぶんdataが配列なのを見逃して0がぬけてるだけやろ。 dig('data',0,'bid')とか? >>129 ヒントを元にそれぞれブラケットで囲ったら行けました。ありがとうございました >>131 大変申し訳ありません。digについて理解しました。重ねてありがとうございました。 度々申し訳ないのですが {"status"=>0, "data"=>[{"amount"=>"0", "available"=>"0", "conversionRate"=>"1", "symbol"=>"JPY"}, {"amount"=>"0", "available"=>"0", "conversionRate"=>"5377836", "symbol"=>"BTC"}], "responsetime"=>"2021-04-24T06:14:41.169Z"} ここから5377836を出力する場合も理解さえできればdigでいけるのですか? 今のコード r_hash = JSON.parse(response.body) puts r_hash["data"][1]["conversionRate"] # =>"5377836" できれば覚えたてのこれ使いたい↓ puts response_hash = dig("data",ここわからない,"conversionRate") ブラケットで書くのと同じだよ r_hash["data"][1]["conversionRate"] r_hash.dig("data", 1, "conversionRate") >>134 ありがとうございます。無事取れたのですが引数が頭の中でぐるぐるしてるのでもう一度リファレンス見てきます つーか、リストとハッシュについての理解が足りないんちゃうか? インデックスとかキーとかの感覚がわからんのやろ。 >>136 おっしゃる通りです。{[{}{}]} この形で返ってくるともう何がなんだか 配列の添え字(数字)が文字列になっただけだよ 添え字が数字じゃないので一づつ増やすことはできないけど >>138 アドバイスありがとうございます。悲しいくらい今日も予定がないので学習のため一個ずつ値を呼び出して確認してみます。 >>139 知ってるかもしれないけど この手のは p よりも pp で表示するとわかりやすいよ Ruby 2.5以降は requireしなくても最初から pp が使える 初心者は、dot install, progate などをやれば? >>140 pp知りませんでした。ありがとうございます。かなり頭の中の世界が変わりました。 プロゲートはいまいちだったのでユーデミーの教材漁ってたのですがもう一度プロゲートやり直してみます 本が好きなら、定番の入門書「たのしいRuby 第6版」2019 とか YouTube で有名な、雑食系エンジニア・KENTA のサロンは皆、Ruby on Rails じゃないの? >>145 「オンライン」とは?サーバーでということか?Windows? Excelはxlsx?csvなら難しくはないが。。。 など、あぶなっかしいところが多すぎ。 初心者がやるにはたいへんやな。w やめといたら? 3つに分離する部分だけを作った。 入出力の部分は、CSV モジュールを使えば? re = %r!https?://! # 正規表現。http/https input = String.new( "https://rilakkumasabo.jp/shop/%E5%B5%90%E5%B1%B1https ://www.telacoya.co.jp/company/shop_detail/shop_detail-130/http://www.arashiyamaryo.or.jp/access/" ; ) positions = [ ] pos = 0 while md = re.match( input, pos ) positions.push md.begin( 0 ) # 一致した先頭文字h の位置 pos = md.end( 0 ) # 一致した末尾文字/ の次の文字の位置 end # 文字列の末尾から削除しながら、配列に入れていく urls = positions.reverse.map { |pos| input.slice!( pos..-1 ) } pp urls.reverse # 反転 出力 ["https://rilakkumasabo.jp/shop/%E5%B5%90%E5%B1%B1" ;, "https://www.telacoya.co.jp/company/shop_detail/shop_detail-130/" ;, "http://www.arashiyamaryo.or.jp/access/" ;] 出来た! 文字列を3分割する部分を関数化して、呼び出す 入出力は、CSV 形式で。 入力ファイルは、input.csv paiza.IO などで実行してみれば? ただし、この前、paizaのソースコードが消えてしまったけど。 定期的に消えるのかな? # 引数の文字列を3分割して、配列に入れて返す def split_string( input_str ) re = %r!https?://! # 正規表現。http/https # 下で、slice! で変更するために、破壊的変更可能文字列へ変換する input = String.new( input_str ) positions = [ ] pos = 0 while md = re.match( input, pos ) positions.push md.begin( 0 ) # 一致した先頭文字・h の位置 pos = md.end( 0 ) # 一致した末尾文字・/ の次の文字の位置 end # 文字列の末尾から削除しながら、配列に入れていく urls = positions.reverse.map { |pos| input.slice!( pos..-1 ) } urls.reverse # 反転 end 次へ続く require 'csv' result_ary = [ ] CSV.foreach( "input.csv" ) do |row| # 1行ずつ処理する split_ary = split_string( row[ 1 ] ) split_ary.each do |url| result_ary.push [ row[ 0 ], url ] end end # 2次元配列を、CSV 文字列に変換する csv_str = result_ary.map( &:to_csv ).join print csv_str paramsという名前のハッシュを関数の中で使う際、 :codeの組をhogeだけでなくmoge、kuge、sageなど 関数の引数で選べるようにする場合、 '+ 引数名 +'だとエラーになるようですが、 バリューに引数を使おうとしている事自体が根本的に間違っていますか? params = { :code=> 'hoge', ←'+ 引数名 +'だとダメ。ここを入れ替えたい :page => 1, :count => 5 } 意味が分からん。 エラーになるコードと希望する出力は何? >>152 APIを使用して住んでる地域の時間ごとの過去の天気を見る学習をするはずだったのですが 自分でも何がしたいのかわからなくなってきたのでちょっと出直します。 すみませんでした。 >>151 変数名と値(リテラル)の区別があやふやなんでは? 変数名をクォートしようとしている時点でヤバイ。 >>148-150 ありがとうございます。 rubyのインストールが上手く出来なかったので明日またやってみます。 >>150 Array#to_csvってかなり非効率的なんで避けたほうが無難 csv_str = CSV.generate{|csv| result_ary.each{|it| csv << it } } Pythonみたいに行けてるエクセルライブラリが欲しい。。 >>160 require "bundler/inline" gemfile do source "https://rubygems.org" ; gem "benchmark_driver" end Benchmark.driver do |x| x.prelude %{require "csv"} x.prelude %{ITEMS = Array.new(1000){ ["fooo", 12345,6789] }} x.report "to_csv", %{ csv_str = ITEMS.map{ _1.to_csv }.join } x.report "&:to_csv", %{ csv_str = ITEMS.map(&:to_csv).join } x.report "generate", %{ csv_str = CSV.generate{|csv| ITEMS.each{|it| csv << it } } } end 計測すると6倍ほどto_csvの方が遅い事がわかる to_csvは https://github.com/ruby/csv/blob/efb257e28f85741fb5c2637f707121e55e4c5ca9/lib/csv/core_ext/array.rb https://github.com/ruby/csv/blob/efb257e28f85741fb5c2637f707121e55e4c5ca9/lib/csv.rb#L1452 見ると分かるがCSV.new等を毎回行う手抜き実装(バグりにくいというメリットは確かにある) 1行だけならともかく複数行をcsv化したいなら微妙 実際にはどうせファイルに書き込むんだからファイルIOがボトルネックになるんで誤差だよ 無意味なベンチマークだね >>162 キャッシュなしフロッピーディスク💾を使ってるのかな?w >>162 Benchmark.driver do |x| x.prelude %{require "csv"} x.prelude %{require "tempfile"} x.prelude %{ITEMS = Array.new(1000){ ["fooo", 12345,6789] }} x.report "to_csv", %{ Tempfile.create{ _1.write ITEMS.map(&:to_csv).join } } x.report "generate", %{ Tempfile.create{ _1.write CSV.generate{|csv| ITEMS.each{|it| csv << it } } }} end 確かに縮まりはしたが うちのオンボロPCでも5倍弱の差が出ました。 c="C:\Program Files\7-Zip\7z.exe" p c で出力は > "C:Program Files\a-Zip\az.exe" となってしまうんですが、なんで7がaに変わってしまってるんでしょうか? 2.6.5p114 (2019-10-01 revision 67812) [x64-mingw32]です https://ja.wikipedia.org/wiki/ ベル文字 > ベル文字は、ASCIIとUnicodeでは十進数で7 > 1972年に作られたC言語では、ベル文字は文字定数 \aで表される。'a'は"alert"や"audible"の頭文字である。'b'は既にバックスペースに使われていた。 >>166 バックスラッシュ記法で8進数表記のパターンとして解釈されるから 式展開が必要なければシングルクウォートにするといい https://docs.ruby-lang.org/ja/3.0.0/doc/spec=2fliteral.html#backslash ¥007も¥07も¥7も同じ C言語由来のエスケープシーケンス >>166 >>168 のシングルクォートよりも、パスの区切り文字を「/」にすることをすすめたい。 パスの区切り文字が「¥」でなければいけないのはコマンドプロンプトなど一部のみ。 だいたいが「/」でも可。 >>167-169 まとレスにて失礼します aに置き換わる謎はわかりました シングルクォートなのですが、面倒くさいことにWindowsの決め打ちで空白が入っているので それを考えるとダブルクォートで囲まざるを得ず、けっきょくこんな形になってしまいました c="\"C:\\Program Files\\7-Zip\\7z.exe\"" このあと system("#{c} a -mx9 ...") みたいな形で呼び出します >>170 c = '"C:\Program Files\7-Zip\7z.exe"' >>171 ああなるほど それで行けますね 昔は自分でもそんな書き方してたかもしれなかったです >>172 ご案内ありがとうございます シングルクォートとダブルクォートの意味とか違いは理解していたのですが 中に\入れたときとか、とくに168さんに指摘されたように8進数での扱いが特別だということは よく理解していなかったので、これを機会に知識を正確にしておきます read.cgi ver 07.5.5 2024/06/08 Walang Kapalit ★ | Donguri System Team 5ちゃんねる