Conversation
Define methods directly without defining separate classes or modules. * 05.wc/lib/wc_methods.rb: Remove file. * 05.wc/lib/word_count.rb: Remove file. * 05.wc/wc.rb: Merge into this file.
* 05.wc/wc.rb (#word_count): 上から順番に読めるようになった。 (#word_count_for_string, #word_count_for_string_per_type): Remove method. (#count_newline, #count_word, #count_bytesize): New method.
| require 'optparse' | ||
|
|
||
| errno = main(ARGV) | ||
| OPTION_STRING = 'lwc' |
There was a problem hiding this comment.
AVAILABLE_OPTION_CHARS とかですかね。下の DEFAULT_OPTION_CHARS と同じ OPTION_CHARS だとわかるほうがよいかなと思います。
05.wc/wc.rb
Outdated
|
|
||
| option_chars = extract_option_chars(options) | ||
|
|
||
| format_string = generate_format_string(args, option_chars) |
There was a problem hiding this comment.
ここでは args が OptionParser.getopts によって破壊的に変更されていることが前提になっていると思います。こういった実行順により暗黙的に状態が変わってしまうようなコードはあとあと見つけづらいバグを埋めこんでしまう可能性が高いため、できるだけ状態変化が明示的にわかるようなコードにしたほうがよいです。
今回であればたとえば、
def parse_commandline_options
options = OptionParser.getopts(ARGV, OPTION_STRING)
[options, ARGV]
endのように、破壊的変更と外部環境(ARGV)への依存を閉じ込めたメソッドを作ってあげて、
options, filenames = parse_commandline_optionsとするなど、できるだけ明示的なコードになるとよいと思います。
05.wc/wc.rb
Outdated
| option_chars = extract_option_chars(options) | ||
|
|
||
| format_string = generate_format_string(args, option_chars) | ||
| results = word_count_results(args, option_chars) |
There was a problem hiding this comment.
何らかの働きをするメソッドは動詞 ( print, load, show など) や動詞 + 目的語 (print_filenames など) という命名にするとわかりやすいです。今回ですと、 count_words_by_options などですかね。
05.wc/wc.rb
Outdated
| option_chars.to_h { [_1, 0] } | ||
| .merge!(*results.filter_map { _1[:count] }) { |_, total, count| total + count } |
There was a problem hiding this comment.
ここもちょっとややこしいので、もうちょっと素朴に書けるかなと思います。
init_value_for_total = option_chars.to_h { [_1, 0] }
count_total = results.each_with_object(init_value_for_total) do |result, total|
option_chars.each do |option|
total[option] += result[:count][option]
end
endなどですかね。
05.wc/wc.rb
Outdated
| end | ||
|
|
||
| def word_count_results(parsed_args, option_chars = DEFAULT_OPTION_CHARS) | ||
| paths = parsed_args.empty? ? ['-'] : parsed_args |
There was a problem hiding this comment.
実際に - というファイル名のファイルがあったら困るので、 - は使わないほうがよいと思います。 nil にするとか、文字列じゃないものにしましょう。
05.wc/wc.rb
Outdated
| word_count['l'] = count_newline(lines) if option_chars.include?('l') | ||
| word_count['w'] = count_word(lines) if option_chars.include?('w') | ||
| word_count['c'] = (file?(path) ? size(path) : count_bytesize(lines)) if option_chars.include?('c') |
There was a problem hiding this comment.
効率は落ちますが、ここではとりあえず全部計算しておいて、オプションによる条件づけは表示時のみでもよいですよ。
05.wc/wc.rb
Outdated
| num = 0 | ||
| line.split { num += 1 if _1.match?(/[[:graph:]]/) } | ||
| num |
There was a problem hiding this comment.
これってたとえば lines.flat_map(&:split).compact.count とかではだめなんでしょうか。
ただ空白で分割して数える、のほうが素直に思いました。
05.wc/wc.rb
Outdated
| end | ||
|
|
||
| def count_newline(lines) | ||
| lines.sum { |line| line.count("\n") } |
05.wc/wc.rb
Outdated
| def calc_digit(parsed_args) | ||
| paths = parsed_args.empty? ? ['-'] : parsed_args | ||
|
|
||
| default_digit = paths.any? { exist_non_regular_file?(_1) } ? 7 : 1 |
There was a problem hiding this comment.
今回はプラクティスなので、通常ファイル以外がわたされた場合はあまり考えなくてよいです。
また、やるとしてもただエラー表示するだけでよいかなと思います。
05.wc/wc.rb
Outdated
| format_string = Array.new(enabled_option_count, "%#{digit}d") | ||
|
|
||
| format_string << '%s' unless parsed_args.empty? | ||
|
|
||
| format_string.join(' ') |
There was a problem hiding this comment.
%dや%sなどの表記は慣れていないと読みづらいので、 rjust などで対応できるならそちらのほうがよいのですが、どうでしょうか。
* 05.wc/wc.rb (#main) <init_value_for_total, count_total>: Separate the initial value and the total processing. (#word_count_results): Create `Hash` simply.
* 05.wc/wc.rb (#main): No arguments are required as `ARGV` is used within the method. (#parse_commandline_options): New method. (#generate_format_string, #calc_digit, #word_count_results): Rename argument.
* 05.wc/wc.rb (#word_count) <buf>: Use `String` instead of `[String]`. (#count_newline, #count_word, #count_bytesize): Stop using `Array#sum`.
* 05.wc/wc.rb (#calc_digit, #word_count_results): Stop treating '-' as standard input. <paths>: Do not rewrite this argument. (#stdin?, #exist_non_regular_file?, #exist?, #regular_file_size) (#file?, #size): Remove unused method. (#word_count): Change the return value to nested `Hash`.
Use `Hash#values_at` or `String#rjust` instead of. * 05.wc/wc.rb (#generate_format_string, #word_count_results) (#print_word_count_result): Remove method. (#main): Extract the process to output the count and its total. <counts>: New variable. (#count_newline_word_byte): Similar to `#word_count`, but counts all items (newline, word, and byte). (#print_counts, #stdin?, #total_counts): New method. (#calc_digit): Change arguments and processing for use with `#print_counts`.
|
@yoshitsugu お疲れさまです。レビューしていただき、ありがとうございます!
|
なるほど。つまり
うーん、それは僕の当初の指摘に答えていないように思います。「こういう実装が既にあるからこうした」というのは、その説明だけ見ると、自分の考えがないように思います。例えば、「今の実装にあるメリットはXXXで、 |
ペアプロでいただいたアドバイスを元に修正した。