文字列から日付に
646 >> Name: デフォルトの名無しさん [sage] posted at: 2008/08/16(土) 17:28:38
以下はC#で書いた、"2003/4"みたいな文字列からDateTimeに変換する関数ですが。
これを関数型言語らしく書くと、どのように書けますか?
従来の思考が抜けなくて、どうやっても関数型言語で手続きしてしまいます。DateTime? parse1(string s)
関数型と手続き型
{
var m = Regex.Match(s, @"^(\d+)/(\d+)$");
if(!m.Success)
return null;
var year = int.Parse(m.Groups[1].Value);
var month = int.Parse(m.Groups[2].Value);
return new DateTime(year, month, 1);
}
時刻用のデータ型は無いっぽいので、構造体でごまかす。今は (make-date :year 200X :month XX :day XX) できればいいやってことで。
(defstruct date year month day)
脱線するけどこんな感じのマクロをたまに書いてしまう。(date 2008 11 21) で (make-date :year 2008 :month 11 :day 21) を評価するマクロ。
(defmacro date (year month day) `(make-date :year ,year :month ,month :day ,day))
そして今勢いだけでこんなもの書いてしまった。上のような構造体のコンストラクタ(?)を (構造体名 引数1 引数2...) で評価するマクロを定義するマクロ。名前適当すぎるだろ・・・。
(defmacro define-constructor-sugar (struct &rest args) `(defmacro ,struct (,@args) (,(intern (concat "make-" (string struct))) ,@(mapcan (lambda (sym) (list (intern (string sym) (find-package "keyword")) sym)) args))))
話を戻して、とりあえず書いてみたらこーなった。let はいらないな。全部 parse-integer って書いてあるのがもにょもにょする。
(defun string->date (string) (let ((re "^\\([0-9]+\\)/\\([0-9]+\\)\\(?:/\\([0-9]+\\)\\)?$")) (if (string-match re string) (make-Date :year (parse-integer (match-string 1)) :month (parse-integer (match-string 2)) :day (parse-integer (or (match-string 3) "1"))))))
書き直し。長くなっちゃった。しかも day を省略できなくなっちゃった。マクロでキーワード不要にして mapcan 無くせば短くなると思ったけど、マクロに apply とかできなかった、しょんぼり。
(defun string->date (string) (if (string-match "^\\([0-9]+\\)/\\([0-9]+\\)\\(?:/\\([0-9]+\\)\\)?$" string) (apply 'make-date (mapcan 'list '(:year :month :day) (mapcar 'parse-integer (mapcar 'match-string '(1 2 3)))))))
正規表現は使わないで書いてみる。やっぱり mapcan 邪魔いなぁ。
(defun string->date (string) (apply 'make-date (mapcan 'list '(:year :month :day) (split-string string "/"))))
関数型っぽいかどうかはよくわからんです。特にオチはないです。