電波ビーチ

☆(ゝω・)v

【ruby】連番をキーとし配列を値にしてるハッシュ

irb(main):016:0> n = 4
=> 4
irb(main):017:0> table = (1..n).to_a.zip(Array.new(n, Array.new)).to_h
=> {1=>[], 2=>[], 3=>[], 4=>[]}
irb(main):018:0> table[1] << 1
=> [1]
irb(main):019:0> table
=> {1=>[1], 2=>[1], 3=>[1], 4=>[1]}

👿

irb(main):030:0> table = (1..n).to_a.map{|i| [i, Array.new]}.to_h
=> {1=>[], 2=>[], 3=>[], 4=>[]}
irb(main):031:0> table[1] << "あばばばばば"
=> ["あばばばばば"]
irb(main):032:0> table
=> {1=>["あばばばばば"], 2=>[], 3=>[], 4=>[]}

👼

Ruby オブジェクト -> hash -> json

アロー演算子じゃないです。

インスタンス変数: 値 みたいな感じで

require 'json'
require 'time'

class Hito
  attr_accessor :age, :name, :id
  def initialize(age, name)
    @age = age
    @name = name
    @id = Time.new.iso8601(6)
  end
end

human = Hash.new

a = Hito.new(392, "dare")
b = Hito.new(49, "kimi")
c = Hito.new(113, "watashi")

# ただハッシュにぶちこんだだけのオブジェクトをjsonizeする
def jsonize(dict)
  ret = Hash.new{|h,k| h[k] = {}}
  dict.map{|k, v|
    ret[k] = v.instance_variables.map{|var| [var, v.instance_variable_get(var)]}.to_h
  }.to_json
  open("hoge.json", 'w') do |io|
    JSON.dump(ret, io)
  end
end

human[a.id] = a
human[b.id] = b
human[c.id] = c

jsonize(human)

ハッシュ内ハッシュ Hash.new{|h,k| h[k] = {}}はこれ

qiita.com

インスタンス変数: 値はこれ

qiita.com

特異メソッドにしたいだけ、ならばmodule_functionは要らない

module_functionを使うと確かに特異メソッドが作れる。が、それで作られるメソッドは、同時にプライベートメソッドでもある。( 参考:伊藤淳一「プロを目指すためのRuby入門 言語仕様からテスト駆動開発デバッグ技法まで」kindle版 , 技術評論社, 位置 7425

hoge.rb

module Hoge
  module_function
  def hoge(n)
     "ほげ"*n
  end
end

main.rb

require_relative 'hoge'

def yobu
  puts Hoge.hoge(3)
end
    
yobu

結果

ほげほげほげ

Hogeには外部から呼び出すためのメソッドだけおいておけばいい、という場合に、プライベートメソッドは不要である。むしろ上記の場合だとmain.rbから呼ぶ際にレシーバを指定する方法がそもそも無いのでプライベートメソッドは必要ない。(ここんとこ理解および日本語があやしいかもしれないので間違ってたらそっと教えてください

解決法

特異クラスにしてやればよい。

hoge.rb

module Hoge
  # module_function <- プライベートメソッドは必要ない
  class << self
    def hoge(n)
       "ほげ"*n
    end
  end
end

参考

qiita.com

rubyの便利コマンドラインパーサoptparseのdescriptionがsummaryなので見やすくしたい

なにをいってるかわからねータイトルですが。

rubyにはoptparseっていうコマンドラインパーサが標準ライブラリ*1があり、使う機会があった

github.com

-hでヘルプを表示できるのだが、たとえばこんなやつ

require "optparse"

OptionParser.new do |opt|
  begin
      # バージョンを追加
      opt.version = "0.0.1"
      # リリース日を追加
      opt.release = "2018-11-12"
      # 名前を追加
      opt.program_name = ""
      # Usageで最初に表示されるやつ
      opt.banner = "Usage: #{opt.program_name} [options] [directory]"
      # helpとかUsageで表示されるときのサマリーが始まるまでの行頭からの幅
      summary_width = 32
      opt.summary_width = summary_width
      # 同じくコマンドのインデント
      summary_indent = 4
      opt.summary_indent = ' ' * summary_indent
      # summaryの幅を改行時にも揃えたい。長文で折り返した際も同じようにインデントするようにしたい
      # opt.onでオプションとそのdescを追加
      opt.on("-x", "所詮サマリーなのでそもそも長文を書くものではないんだけど、それにしてもターミナル幅が短いとめちゃくちゃ読みにくいしつらい。\n改行なんてしようもんならもっとひどいことになる。とても読めたものではない"){ |v|
          options[:a] = v
      }
      opt.separator("")
      opt.on("-h", "--help", "show help."){
          puts opt.help
          exit
      }
      opt.on("-v", "--version", "show version."){
          puts opt.ver
          exit
      }
      opt.parse!(ARGV)
  rescue OptionParser::InvalidOption => e
      puts e.message
      return
  end
end

を、ヘルプオプションで表示しようとすると、ターミナル幅が70とかだとこんな感じになっちゃう f:id:or3:20181112101242p:plain

これがストレスなのでちゃんとインデント付きで折り返してくれるようなヘルパー関数を作ってあげた

github.com

(中見ればわかりますが力ずくでよろしくやっているだけ...)

*1:rubyの文化知らんのだが組み込みgem?でいいのかな

【AOJ】Rubyで2次元配列を連番で初期化 && Rubyでpythonのisdigitっぽいやつ

AOJ ITP1_6_B

タイトルのやつでおいしい書き方ができずに少しハマった。

2次元配列を連番で初期化

map内での宣言で要素を初期化できる。範囲オブジェクトをto_aで配列にしてぶちこんだ。

cards = Array.new(4).map { Array.new((1..13).to_a) }
=> [[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]]

isdigitっぽいやつ

pythonだとよくある感じの書き方で、文字列のリストの要素の中で数値に変換できるやつは変換してしまう、というやつはisnumericを使う。

target = list(map(lambda a: int(a) if a.isnumeric() else a, input().split()))

べつにisdigitでもいいんだけど、なんかのやつでなぜか漢数字を変換しなくちゃいけない場面があって以来isnumeric万能ってなったので使っている。

kk6.hateblo.jp

Rubyにはそんな便利なメソッドは無いらしい。調べると正規表現でやっているのをよく見かけるので真似した。

konbu13.hatenablog.com

こちらのをパクらせていただきました。ありがとうございます。

今回はfloatにも対応するようにしてみた。三項演算子入れ子にしたのだが、いちいちmatchを判断しているところが冗長で遅そう。正規表現力が圧倒的に足りない。

arr = ["unk0", "321", "0.00220", "y00011", "321.32xx334" "127.0.0.1"]
arr.map { |t| t.match? (/^\d+\.?\d+$/) ? (t.match? (/\./) ? t.to_f : t.to_i) : t }
=> ["unk0", 321, 0.0022, "y00011", "321.32xx334", "127.0.0.1"]

mkdirで連番ディレクトリの作成

あらすじ

直近のABC1071完だった。酒が入っていた&&出先で適当に暇つぶしのつもりだった&&時間が30分ほどしかなかったとはいえ、半年ほどろくにコードを書いてないだけでこれほどカスに成り果ててしまった。とてもかなしいのでAOJ Introduction fungosを始めようと思う。今作った造語だが、AOJ Introduction to Programmingをただやるだけである。

これだけ

で、やろうと思って、とりあえず環境を作った。ITP1はITP1_1からITP1_18まで、各AからDまでの問題がある。それらをディレクトリごとで分ける。プロジェクトルートで

mkdir -p ITP1/ITP1_{1..18}{A..D}

これで環境は整った。今日から毎日やっていく。