Project Euler Problem 22
問題22: ファイルに保存されている全ての名前のスコアを計算しその合計を求めよ。[=>問題文]
問題22の解答
;; Emacs Lisp (require 'cl) (defun problem022 (file-path) (labels ((read-names () ;; ファイルから名前リスト読み込み (let ((tmp) (buffer (find-file file-path))) (replace-string "\"" "") (goto-char (point-min)) (replace-string "," " ") (setq tmp (buffer-string)) (set-buffer-modified-p nil) ; 変更フラグクリア (kill-buffer buffer) (split-string tmp)))) (let ((names (sort (read-names) #'string<)) ; 読み込んでソート (i 0) (code-base (1- ?A))) ;; スコア計算 (apply #'+ (cons 0.0 (mapcar #'(lambda (name) (* (incf i) (reduce #'(lambda (acc c) (+ acc (- c code-base))) name :initial-value 0))) names)))))) (problem022 "names.txt")
ファイルの内容を名前のリストにパースするためには。「カンマで分割」「ダブルクォートの除去」が必要です。Emacs Lisp でのやり方を調べたり試したりした結果、バッファに全部読み込んで replace-string でカレントバッファを編集し、lispで読み込みやすい形に整形してから文字列処理をする方法が一番簡単なようです。
compile-defun でコンパイルすると、「replace-string は対話的操作で使う関数だぞ」と警告が出ますが、とりあえず問題なく機能しているようです。
リストにしてしまえばもっと楽(?)
「lispで読みやすい形に整形」をもう一歩進めて、バッファ内文字列をリストの形に編集してみました。
(defun problem022 (file-path) (labels ((read-names () (let ((lis) (buffer (find-file file-path))) (insert "(") (replace-string "," " ") (goto-char (point-max)) (insert ")") (goto-char (point-min)) (setq lis (read buffer)) (set-buffer-modified-p nil) (kill-buffer buffer) lis))) ;; 以下略
バッファの最初に "(" を挿入して、","をスペースに置換。そしてバッファ最後に ")"を挿入。あとは read 一発でリストとして全データを読み込みます。