Emacs Lispで「ソフトウェアエンジニアならば1時間以内に解けなければいけない5つの問題」を解いてみた
http://mattn.kaoriya.net/software/vim/20150527121332.htm
ちょうど 1時間程度. 1-4はすぐに書けて, 5に時間を要した.
(追記: 2015年6月1日 問4に誤りがあり修正しました)
問題1
'for'の代わりに loopで.
(require 'cl-lib) (defun problem1-for (lst) (cl-loop for i in lst sum i)) (defun problem1-while (lst) (let ((sum 0) (xs lst)) (while xs (setq sum (+ sum (car xs)) xs (cdr xs))) sum)) (defun problem1-recursive (lst) (if (null lst) 0 (+ (car lst) (problem1-recursive (cdr lst))))) (defun problem1-tail-recursive (lst) (cl-labels ((func (lst acc) (if (null lst) acc (func (cdr lst) (+ acc (car lst)))))) (func lst 0))) (let ((input '(1 2 3 4 5 6 7 8 9 10))) (cl-assert (= 55 (problem1-for input) (problem1-while input) (problem1-recursive input) (problem1-tail-recursive input))))
問題2
(require 'cl-lib) (defun problem2 (xs ys) (cl-loop for x in xs for y in ys collect x collect y)) (cl-assert (equal '(a 1 b 2 c 3) (problem2 '(a b c) '(1 2 3))))
他には mapcanを使うとかだろか.
(cl-mapcan (lambda (a b) (list a b)) '(1 2 3) '(a b c))
問題3
表現範囲の問題で, calcを使用. すぐ書けたものでは正しく表示できず.
(require 'cl-lib) (require 'calc-arith) (defun bignum-to-string (n) (let ((sign (if (eq (car n) 'bigpos) "" "-"))) (concat sign (mapconcat #'number-to-string (reverse (cdr n)) "")))) (defun problem3 (n) (cond ((= n 1) '(0)) ((= n 2) '(0 1)) (t (cl-loop with acc = '(1 0) repeat (- n 2) do (push (calcFunc-add (cl-first acc) (cl-second acc)) acc) finally return (reverse (mapcar (lambda (e) (if (listp e) (bignum-to-string e) e)) acc)))))) (cl-assert (string= (nth 99 (problem3 100)) "218922995834555169026"))
問題4
(1つめのコードは誤っています.)
(require 'cl-lib) (defun problem4 (lst) (let ((xs (sort lst (lambda (a b) (string< (number-to-string b) (number-to-string a)))))) (string-to-number (mapconcat #'number-to-string xs "")))) (cl-assert (= (problem4 '(50 2 1 9)) 95021))
追記: 2015/JUN/1 修正版
(require 'cl-lib) (defun compare (a b) (let ((ab-val (string-to-number (format "%s%s" a b))) (ba-val (string-to-number (format "%s%s" b a)))) (> ab-val ba-val))) (defun problem4 (lst) (let ((xs (sort lst #'compare))) (string-to-number (mapconcat #'number-to-string xs "")))) (cl-assert (= (problem4 '(420 42 423)) 42423420) (= (problem4 '(50 2 1 9)) 95021))
問題5
他はすぐに思いついたけど, これは時間がかかった.
evalは使わず. 演算子の全組み合わせを出せばすぐだなと思ったけど,
文字列連結を先にするのがうまくいかず, 苦労した.
(require 'cl-lib) (defun concat-num (a b) (string-to-number (concat (number-to-string a) (number-to-string b)))) (defun permute-ops (n) (let ((accs nil)) (cl-labels ((permute-ops- (n i acc) (if (= n i) (push (reverse acc) accs) (cl-loop for op in '(+ - concat-num) do (permute-ops- n (1+ i) (cons op acc)))))) (permute-ops- n 0 '()) accs))) (defun apply-concat (lst ops) (cl-loop with stack = (list (car lst)) for e in (cdr lst) for op in ops if (eq op 'concat-num) do (let ((a (pop stack))) (push (concat-num a e) stack)) else do (push e stack) finally return (reverse stack))) (defun calc-ops (lst- ops-) (let ((lst (apply-concat lst- ops-)) (ops (cl-loop for op in ops- unless (eq op 'concat-num) collect op))) (if (null ops) ;; all ops are concat (car lst) (cl-loop with acc = (funcall (car ops) (cl-first lst) (cl-second lst)) for op in (cdr ops) for num in (cddr lst) do (setq acc (funcall op acc num)) finally return acc)))) (defun format-ops (lst ops) (concat (number-to-string (car lst)) (cl-loop for op in ops for num in (cdr lst) if (eq op 'concat-num) concat (number-to-string num) else concat (format "%s%s" op num)))) (defun problem5 (lst) (let ((opss (permute-ops (1- (length lst))))) (cl-loop for ops in opss when (= (calc-ops lst ops) 100) collect (format-ops lst ops)))) (cl-assert (member "1+2+34-5+67-8+9" (problem5 '(1 2 3 4 5 6 7 8 9))))