🎾

Racket: Racket における入出力

2024/07/12に公開

はじめに

この記事では、関数型プログラミング言語であるRacketの基本的な入出力関数について、具体的なサンプルプログラムを用いて説明します。
これにより、Racketプログラムで入出力を実装し、実際のプロジェクトに応用できます。

ここでは、Racketの主要な入出力関数を紹介し、それぞれの機能と使用例を説明しています。
詳細は Racket Reference を参照して、その他の関数や使用例を確認してください。

1. ポートと標準入出力

Racketの入出力関数は、ポートを指定して使用できます。
ポートは、ファイル、ネットワークなどの入出力を統一して管理するRacketの重要なオブジェクトです。
Racketの入出力関数は、ポート越しにデータを入力、出力します。
ポートを指定しないときは、標準入力/標準出力ポートが使用されます。

read関数は、ポートを指定しない場合、標準入力からデータを読み込みます。
write, print, displayの各関数は、ポートを指定しない場合、標準出力にデータを出力します。

通常の入出力関数を使った (ポートを指定しない) Racketプログラムは、コンソールからの入力やパイプ・リダイレクトに対応しています。
これにより、ユーザーはプログラムの入出力を柔軟に操作できます。

2. 入力関数

2.1 read関数の詳細

read関数は、入力データをRacketのデータ型として解釈して返します。
入力データがRacketのデータとして正しくない場合、エラーが発生します。
たとえば、リストの()ではなく]で閉じられている場合に、エラーが発生します。

下記のプログラムは、ユーザーからの入力を読み取り、その入力とデータ型を出力します。
EOF (End of File) (Windowsでは、Ctrl+Zキー)を入力すると、終了します。

このプログラムを実行すると、下記のようになります:

> racket .\echo-loop.rkt

--- echo ---
input: abc
output: 'abc  -  symbol
input: 42
output: 42  -  number
input: "hello"
output: "hello"  -  string
input: #\k
output: #\k  -  character
input: [1 . 2]
output: '(1 . 2)  -  pair
input: '(1 2 3)
output: ''(1 2 3)  -  list
input: ^Z
output: #<eof>  -  unknown

2.2 ファイルからの入力

ファイルからデータを読み取るには、ファイルポートを使用します。
ファイル入力の例は、次のようになります:

fileinput.rkt
(define fin (open-input-file "data.txt"))
(define data (read fin))
(printf "read data: ~a\n" data)
(close-input-port fin)

2.3 文字の入力

read関数には、文字入力用のread-char関数や行入力用のread-line関数があります。
read-char関数は、標準入力から 1文字ずつ読み込み、read-line関数は改行までの 1行を読み込みます。

このとき、文字はUTF-8エンコードで読み込まれます。

詳細は、 Racket Referenceを参照してください。

3. 出力関数

3.1 write/print/displayの違い

write,print,displayの各関数は役割が違います。
それぞれの役割は、次の通りです:

  • write:
    readで読み込んだデータを、そのままの形式で出力する。
  • print:
    REPLでの出力のようにデータを出力する。文字列、シンボルなどはクオートされる。
  • display:
    画面出力用に、データを出力する。文字列型、シンボル型、文字型などはクオートしない。

出力例は、下記を参照してください:

write print display
1/2 (数値型) 1/2 1/2 1/2
#\x (文字型) #\x #\x x
"hello" (文字列型) "hello" "hello" hello
'|tea pot| (シンボル型) '|tea pot| '|tea pot| tea pot
'("I" pod) (リスト型) '("I" pod) '("I" pod) I pod
write (procedure) #<procedure:write> #<procedure:write> #<procedure:write>

3.2 ファイルへの出力

ファイルポートを指定すると、データをファイルに出力します。
ファイル出力には、open-output-fileを使用します。

ファイルがすでに存在する場合、open-output-file関数は例外を発生させますが、#:exists 'truncateオプションを指定することで回避できます。
このとき、Racketは、既存のファイルの内容をすべて消去し、新しい内容で上書きします。
'truncateの代わりに、#:exist 'can-updateが指定できます。
この場合、ファイルの内容は消去せず、新しい内容で上書きします。

プログラムは、次のようになります:

fileout.rkt
(define fout (open-output-file "data.txt"))    ; cause exception if file is already exist
(display "howdy" fout)
(close-output-port fout)

ファイルがすでに存在する場合は、次のようなエラーが発生しますが、#:exist 'truncateオプションで回避できます:

> racket fileout.rkt

open-output-file: file exists
  path: data.txt
  context...:
   body of
   "fileout.rkt"

上記のエラーを回避するために、#:exists 'truncateを指定します:

fileout.rkt
(define fout (open-output-file "data.txt" #:exist 'truncate))
(display "howdy" fout)
(close-output-port fout)

3.3 文字の出力

read-charに対応する形で、1文字出力用にwrite-char関数があります。
write-char関数はread-charで読み込んだ 1文字を出力します。

read-line関数は、通常のwrite関数を使って出力します。

おわりに

この記事では、Racketの入出力関数について基本的なことを説明しました。
これらの関数を駆使することで、外部からのデータ入力などに対応した実践的なプログラムを作成できます。

Racketの学習を深め、関数型プログラミング言語の技能を向上させ、より洗練したプログラムを書けるようになりましょう。

それでは、Happy Hacking!

技術用語と注釈

技術用語について、その注釈を箇条書きにします。

  • read関数:
    入力されたデータを、Racketのデータとして解釈し、型付けされたデータとして返す関数。

  • write関数:
    Racketのデータ型を元の形式で出力する関数。

  • print関数:
    RacketのデータをREPLで使用される形式で出力する関数。

  • display関数:
    データをクオートしないシンプルな形式で出力する関数。

  • ポート:
    データを関数を通じて入出力するための、Racketのオブジェクト。

  • ファイルポート:
    ファイルへの入出力を行なうためのポート。

参考資料

Webサイト

GitHubで編集を提案

Discussion