Skip to content

Instantly share code, notes, and snippets.

@hyuki

hyuki/chat.md Secret

Last active April 19, 2023 20:55
Show Gist options
  • Select an option

  • Save hyuki/7113ad1d97ebbb981891fb3ad91e3911 to your computer and use it in GitHub Desktop.

Select an option

Save hyuki/7113ad1d97ebbb981891fb3ad91e3911 to your computer and use it in GitHub Desktop.
#ChatGPT と結城浩の対話: Rubyでデータ入力プログラムを作ってください。

Model: GPT-4

Date: 2023-04-20

Rubyを使い、コマンドラインで文字列を入力する対話的なプログラムを作ろうと思います。そのひながたを作ってください。一行入力するごとにプログラムは入力したものをエコーバックする仕様とします。一行入力を繰り返して、最後にdoneと入力すると、入力した文字列を(改行付きで)data.txtというファイルに保存して終了します。エコーバックしたものは保存する必要はありません。

以下のRubyプログラムは、指定された仕様に従って対話的に文字列を入力し、最後に 'done' が入力されると、入力された文字列を改行付きで 'data.txt' ファイルに保存します。

# ファイル名: interactive_input.rb

# 入力された文字列を格納する配列
input_lines = []

puts "コマンドラインで文字列を入力してください。入力が終わったら 'done' と入力してください。"

loop do
  # ユーザーからの入力を取得
  input = gets.chomp

  # 入力が 'done' の場合、ループを終了
  break if input == 'done'

  # 入力された文字列をエコーバック
  puts "入力: #{input}"

  # 入力された文字列を配列に追加
  input_lines << input
end

# 入力された文字列を改行付きでファイルに書き込む
File.open('data.txt', 'w') do |file|
  input_lines.each { |line| file.puts(line) }
end

puts "入力された文字列が 'data.txt' ファイルに保存されました。"

上記のプログラムを interactive_input.rb として保存し、コマンドラインで ruby interactive_input.rb と入力して実行できます。このプログラムは、一行入力するごとに入力したものをエコーバックし、done と入力されると入力した文字列を(改行付きで)data.txt ファイルに保存して終了します。

いまのプログラムを改良して、次のようにしてください。(1)ループの一回で3項目(つまり3行)の入力を行う。この3項目はN項目に拡張しやすくしておきます。(2)項目には名前(項目A,項目B,項目C)があり、ループを繰り返す前にそれを表示する。(3)項目にはそれぞれ3種類の定義済み入力があり、数字一文字入れれば、それが入力される。(項目Aを入力中に1を入れれば、A-1と入力される)(4)項目の名前を表示するときに、定義済み入力を表示する。

以下のRubyプログラムは、指定された仕様に従って対話的に文字列を入力し、最後に 'done' が入力されると、入力された文字列を改行付きで 'data.txt' ファイルに保存します。

# ファイル名: interactive_input_v2.rb

# 入力された文字列を格納する配列
input_lines = []

# 項目の名前と定義済み入力を持つハッシュ
items = {
  "項目A" => ["A-1", "A-2", "A-3"],
  "項目B" => ["B-1", "B-2", "B-3"],
  "項目C" => ["C-1", "C-2", "C-3"]
}

puts "各項目に対応する数字を入力してください。入力が終わったら 'done' と入力してください。"

while true
  input_group = []

  items.each do |item_name, predefined_inputs|
    puts "#{item_name} (1:#{predefined_inputs[0]}, 2:#{predefined_inputs[1]}, 3:#{predefined_inputs[2]})"

    input = gets.chomp

    break if input == 'done'

    input = input.to_i - 1
    input = 0 if input < 0 || input >= predefined_inputs.length

    input_group << predefined_inputs[input]
  end

  break if input_group.include?('done')

  input_lines << input_group.join(", ")
end

# 入力された文字列を改行付きでファイルに書き込む
File.open('data.txt', 'w') do |file|
  input_lines.each { |line| file.puts(line) }
end

puts "入力された文字列が 'data.txt' ファイルに保存されました。"

上記のプログラムを interactive_input_v2.rb として保存し、コマンドラインで ruby interactive_input_v2.rb と入力して実行できます。このプログラムは、指定された要件に従って、ループの一回で3項目の入力を行い、各項目には名前と定義済み入力が表示され、数字を入力することで定義済み入力が選択されるようになっています。また、項目はN項目に簡単に拡張できるようになっています。

途中で入力をチェックする部分がありますが、もしも一文字の数字ではなければ、入力された文字列そのものを格納するように変更してください。

指定された要件に従って、入力が1文字の数字でない場合に入力された文字列そのものを格納するようにプログラムを変更しました。

# ファイル名: interactive_input_v3.rb

# 入力された文字列を格納する配列
input_lines = []

# 項目の名前と定義済み入力を持つハッシュ
items = {
  "項目A" => ["A-1", "A-2", "A-3"],
  "項目B" => ["B-1", "B-2", "B-3"],
  "項目C" => ["C-1", "C-2", "C-3"]
}

puts "各項目に対応する数字を入力してください。入力が終わったら 'done' と入力してください。"

while true
  input_group = []

  items.each do |item_name, predefined_inputs|
    puts "#{item_name} (1:#{predefined_inputs[0]}, 2:#{predefined_inputs[1]}, 3:#{predefined_inputs[2]})"

    input = gets.chomp

    break if input == 'done'

    if input.length == 1 && input =~ /^[1-9]$/
      input_index = input.to_i - 1
      input_index = 0 if input_index >= predefined_inputs.length
      input_group << predefined_inputs[input_index]
    else
      input_group << input
    end
  end

  break if input_group.include?('done')

  input_lines << input_group.join(", ")
end

# 入力された文字列を改行付きでファイルに書き込む
File.open('data.txt', 'w') do |file|
  input_lines.each { |line| file.puts(line) }
end

puts "入力された文字列が 'data.txt' ファイルに保存されました。"

上記のプログラムを interactive_input_v3.rb として保存し、コマンドラインで ruby interactive_input_v3.rb と入力して実行できます。このプログラムでは、入力が1文字の数字でない場合、入力された文字列そのものが格納されます。

joinした結果をinput_linesに追加する前に、追加する内容をいったん表示して「これを追加します。いいですか[Y/n]」と尋ね、yまたはYまたは改行ならば追加し、nならば追加せずに次のループに入るように変更してください。

指定された要件に従って、追加する内容を表示し、ユーザーが確認して追加するかどうかを選択できるようにプログラムを変更しました。

# ファイル名: interactive_input_v4.rb

# 入力された文字列を格納する配列
input_lines = []

# 項目の名前と定義済み入力を持つハッシュ
items = {
  "項目A" => ["A-1", "A-2", "A-3"],
  "項目B" => ["B-1", "B-2", "B-3"],
  "項目C" => ["C-1", "C-2", "C-3"]
}

puts "各項目に対応する数字を入力してください。入力が終わったら 'done' と入力してください。"

while true
  input_group = []

  items.each do |item_name, predefined_inputs|
    puts "#{item_name} (1:#{predefined_inputs[0]}, 2:#{predefined_inputs[1]}, 3:#{predefined_inputs[2]})"

    input = gets.chomp

    break if input == 'done'

    if input.length == 1 && input =~ /^[1-9]$/
      input_index = input.to_i - 1
      input_index = 0 if input_index >= predefined_inputs.length
      input_group << predefined_inputs[input_index]
    else
      input_group << input
    end
  end

  break if input_group.include?('done')

  new_input_line = input_group.join(", ")
  puts "これを追加します: #{new_input_line}。いいですか?[Y/n]"
  user_confirmation = gets.chomp

  input_lines << new_input_line if user_confirmation.downcase == 'y' || user_confirmation == ''

end

# 入力された文字列を改行付きでファイルに書き込む
File.open('data.txt', 'w') do |file|
  input_lines.each { |line| file.puts(line) }
end

puts "入力された文字列が 'data.txt' ファイルに保存されました。"

上記のプログラムを interactive_input_v4.rb として保存し、コマンドラインで ruby interactive_input_v4.rb と入力して実行できます。このプログラムでは、追加する内容を表示し、ユーザーが確認して追加するかどうかを選択できます。y、Y、または空白(改行)が入力された場合に限り、追加されます。n が入力された場合、追加せずに次のループに入ります。

ありがとうございました。たいへん助かります。

どういたしまして!お力になれてうれしいです。もし今後も質問やプログラムに関するお手伝いが必要でしたら、どうぞお気軽にお問い合わせください。お手伝いできることがあれば喜んで対応させていただきます。

v4を動かしてみて不具合を見つけたので修正をお願いします。(1)doneを入力したときに終了せずに次のループに入ってしまいますので、ファイルへの追加確認処理に移るようにしてください。(2)項目Aの入力でdoneを入力したときに項目Bの入力に進んでしまいますので、そちらには進まず、ファイルへの追加確認処理に進んでください。

ご指摘いただいた不具合を修正しました。done を入力した際に、終了処理に適切に移行するように変更しました。

# ファイル名: interactive_input_v5.rb

# 入力された文字列を格納する配列
input_lines = []

# 項目の名前と定義済み入力を持つハッシュ
items = {
  "項目A" => ["A-1", "A-2", "A-3"],
  "項目B" => ["B-1", "B-2", "B-3"],
  "項目C" => ["C-1", "C-2", "C-3"]
}

puts "各項目に対応する数字を入力してください。入力が終わったら 'done' と入力してください。"

loop do
  input_group = []
  done_flag = false

  items.each do |item_name, predefined_inputs|
    puts "#{item_name} (1:#{predefined_inputs[0]}, 2:#{predefined_inputs[1]}, 3:#{predefined_inputs[2]})"

    input = gets.chomp

    if input == 'done'
      done_flag = true
      break
    end

    if input.length == 1 && input =~ /^[1-9]$/
      input_index = input.to_i - 1
      input_index = 0 if input_index >= predefined_inputs.length
      input_group << predefined_inputs[input_index]
    else
      input_group << input
    end
  end

  break if done_flag

  new_input_line = input_group.join(", ")
  puts "これを追加します: #{new_input_line}。いいですか?[Y/n]"
  user_confirmation = gets.chomp

  input_lines << new_input_line if user_confirmation.downcase == 'y' || user_confirmation == ''

end

# 入力された文字列を改行付きでファイルに書き込む
File.open('data.txt', 'w') do |file|
  input_lines.each { |line| file.puts(line) }
end

puts "入力された文字列が 'data.txt' ファイルに保存されました。"

上記のプログラムを interactive_input_v5.rb として保存し、コマンドラインで ruby interactive_input_v5.rb と入力して実行できます。このプログラムでは、done を入力した際に、適切に終了処理に移行し、項目Aの入力で done を入力した場合には、項目Bの入力に進まず、ファイルへの追加確認処理に進むように修正されています。

done_flagを使うのはやめて、itemsのループを関数として切り出し、その関数の戻り値によってループを抜けるかどうかを判断してください。

ご指示に従い、items のループを関数として切り出し、戻り値によってループを抜けるかどうかを判断するようにプログラムを変更しました。

# ファイル名: interactive_input_v6.rb

# 入力された文字列を格納する配列
input_lines = []

# 項目の名前と定義済み入力を持つハッシュ
items = {
  "項目A" => ["A-1", "A-2", "A-3"],
  "項目B" => ["B-1", "B-2", "B-3"],
  "項目C" => ["C-1", "C-2", "C-3"]
}

def get_input_group(items)
  input_group = []

  items.each do |item_name, predefined_inputs|
    puts "#{item_name} (1:#{predefined_inputs[0]}, 2:#{predefined_inputs[1]}, 3:#{predefined_inputs[2]})"
    input = gets.chomp

    return nil if input == 'done'

    if input.length == 1 && input =~ /^[1-9]$/
      input_index = input.to_i - 1
      input_index = 0 if input_index >= predefined_inputs.length
      input_group << predefined_inputs[input_index]
    else
      input_group << input
    end
  end

  input_group
end

puts "各項目に対応する数字を入力してください。入力が終わったら 'done' と入力してください。"

loop do
  input_group = get_input_group(items)

  break if input_group.nil?

  new_input_line = input_group.join(", ")
  puts "これを追加します: #{new_input_line}。いいですか?[Y/n]"
  user_confirmation = gets.chomp

  input_lines << new_input_line if user_confirmation.downcase == 'y' || user_confirmation == ''
end

# 入力された文字列を改行付きでファイルに書き込む
File.open('data.txt', 'w') do |file|
  input_lines.each { |line| file.puts(line) }
end

puts "入力された文字列が 'data.txt' ファイルに保存されました。"

上記のプログラムを interactive_input_v6.rb として保存し、コマンドラインで ruby interactive_input_v6.rb と入力して実行できます。このプログラムでは、items のループが get_input_group という関数に切り出され、戻り値によってループを抜けるかどうかが判断されるようになっています。done を入力した際に、適切に終了処理に移行します。

はい、ありがとうございました。

どういたしまして!お力になれてうれしいです。今後も何か質問やプログラムに関するお手伝いが必要でしたら、どうぞお気軽にお問い合わせください。お手伝いできることがあれば喜んで対応させていただきます。

ここでは普通にgetsを使っていますが、行内編集ができる関数はありませんでしたか。プログラム全体を再掲する必要はありません。関数名だけ教えてください。

Rubyで行内編集ができる関数として、Readline.readline を使用できます。Readline モジュールは、GNU Readline ライブラリを使用してコマンドラインでの履歴や行内編集機能を提供します。これを使うには、まず require 'readline' で Readline モジュールをロードする必要があります。

以下は、Readline.readline を使用して行内編集を可能にする方法です。

require 'readline'

input = Readline.readline("プロンプト: ", true)

このコードは、プロンプトを表示し、ユーザーが入力した内容を input 変数に格納します。行内編集は、矢印キーを使用して行の移動や編集ができる機能です。true を引数に指定すると、入力履歴が有効になります。

この機能を interactive_input_v6.rb の gets の代わりに使用するには、Readline.readline を適切な場所で呼び出すようにコードを変更してください。

ありがとうございます。

どういたしまして!もし今後も質問やプログラムに関するお手伝いが必要でしたら、どうぞお気軽にお問い合わせください。お手伝いできることがあれば喜んで対応させていただきます。良い一日をお過ごしください!

@hyuki
Copy link
Copy Markdown
Author

hyuki commented Apr 19, 2023

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment