About

有时候为了自己写的代码删起来还挺麻烦的, 可能偶尔可能会写出一些可以复用的, 但是大多数时候写的都是一些用完就扔的代码. 不过虽然这么说, 但是实际上写多了之后, 就会发现其实也不是不能复用, 只是暂时还没有发现有相关的应用方向.

之后会考虑把自己的写的代码 (大部分都在 scratch.lisp 里面, 一些比如说是在写项目的时候, 发现某些函数/宏很适合保留下来以后复用) 都尽量收集在一个地方: RYO (Github).

但是可能还是会有一些实在没法分类的, 但是也还挺好玩的, 就留到这样 post 里面吧.

iter-i*

在写计算物理的时候, 发现 lparallelpdotimes 的过程里面重复嵌套, 比如:

(pdotimes (i 5)
  (pdotimes (j 5)
    ...))

貌似会出现一个卡死的情况, 于是就写了一个 piter-i* 的函数 (piter-i* (L135)):

(piter-i* ((i j) (j 5))
  (do-something-with i j))

实际上的过程类似于串行提交计算任务, 然后等待计算结果的出来:

(let ((channel (lparallel:make-channel))
      (count   0))
  (iter-i* ((i j) (j 5))
    (incf count)
    (lparallel:submit-task
     channel (lambda (i j) (do-something-with i j)) i j))
  (dotimes (i count) (lparallel:receive-result channel)))

二次元的数学题

看到高中老师放出来的小孩受苦题 (奥数), 很是快活, 隧写程序爆破之.

然数日, 观 南家三姐妹, 何だけ, 出现了一模一样的题目诶.

/_img/pieces/minami-ke-quiz.jpg

注: 不过我把原来的 enum 函数换成了 defenum (defenum (L3)).

原来的形式

是用的是 lambda, 实际上使用起来还是有点麻烦的:

(defmacro enum (&rest keyword-index-pairs)
  "Make an enumator lambda. "
  `(lambda (keyword)
     (ecase keyword
       ,@(loop for index from 0
               for key-idx in keyword-index-pairs
               for pair? = (listp key-idx)
               for key = (if pair? (first key-idx) key-idx)              
               do (when pair? (setf index (second key-idx)))
               collect `(,key ,index)))))

(defparameter *周*
  (enum :一 :二 :三 :四 :五 :六 :天))

现在如下定义即可:

(defenum 周? :一 :二 :三 :四 :五 :六 :天)

解题思路:

  • 注意到解空间大小为 7, 是一个小量, 可以枚举
  • 只需要列出以 (周? n) 开始的日历, 并直接数数即可, 判定函数 count-day 的复杂度为常数时间

列出日历:

(defun make-month-calender (start-day-in-week &optional (days-in-month 31))
  "Make a calender in 2D array. "
  (let* ((start (if (integerp start-day-in-week) start-day-in-week
                    (周? start-day-in-week)))
         (weeks (ceiling (+ start days-in-month) 7))
         (calender (make-array (list weeks 7)))
         (day 0))
    (iter-i* ((weekday 7) (week weeks))
      :reject (lambda (weekday week)
                (or (and (= week 0) (< weekday start))
                    (>= day days-in-month)))
      (setf (aref calender week weekday) (incf day)))
    calender))
可视化
(defun print-calender (calender &optional (stream *standard-output*))
  (format-table stream
                (collect-i* ((weekday 7) (week (array-dimension calender 0)))
                  (aref calender week weekday))
                :headers '("Mo" "Tu" "We" "Th" "Fr" "Sa" "Su")))

(print-calender (make-month-calender :天))
| Mo | Tu | We | Th | Fr | Sa | Su |
|----|----|----|----|----|----|----|
| 0  | 0  | 0  | 0  | 0  | 0  | 1  |
| 2  | 3  | 4  | 5  | 6  | 7  | 8  |
| 9  | 10 | 11 | 12 | 13 | 14 | 15 |
| 16 | 17 | 18 | 19 | 20 | 21 | 22 |
| 23 | 24 | 25 | 26 | 27 | 28 | 29 |
| 30 | 31 | 0  | 0  | 0  | 0  | 0  |

凎, 中文它不等宽… 之后再想办法. 总之现在换成英文.

数日历中某天的出现次数:

(defun count-day (calender count-day-in-week)
  (let ((weekday (if (integerp count-day-in-week) count-day-in-week
                     (周? count-day-in-week)))
        (count 0))
    (dotimes (week (array-dimension calender 0) count)
      (unless (zerop (aref calender week weekday))
        (incf count)))))

于是就解决了:

(loop for start-day in '(:一 :二 :三 :四 :五 :六 :天)
      for calender  = (make-month-calender start-day)
      for wednesday = (count-day calender :三)
      for tuesday   = (count-day calender :二)
      do (format t
                 "~&~%开始: 周~a, 周三次数 ~d 次, 周二次数 ~d 次, ~:[不满足~;满足~]"
                 start-day wednesday tuesday (and (= wednesday 4) (= tuesday 5)))
      do (print-calender calender))

开始: 周一, 周三次数 5 次, 周二次数 5 次, 不满足
| Mo | Tu | We | Th | Fr | Sa | Su |
|----|----|----|----|----|----|----|
| 1  | 2  | 3  | 4  | 5  | 6  | 7  |
| 8  | 9  | 10 | 11 | 12 | 13 | 14 |
| 15 | 16 | 17 | 18 | 19 | 20 | 21 |
| 22 | 23 | 24 | 25 | 26 | 27 | 28 |
| 29 | 30 | 31 | 0  | 0  | 0  | 0  |

开始: 周二, 周三次数 5 次, 周二次数 5 次, 不满足
| Mo | Tu | We | Th | Fr | Sa | Su |
|----|----|----|----|----|----|----|
| 0  | 1  | 2  | 3  | 4  | 5  | 6  |
| 7  | 8  | 9  | 10 | 11 | 12 | 13 |
| 14 | 15 | 16 | 17 | 18 | 19 | 20 |
| 21 | 22 | 23 | 24 | 25 | 26 | 27 |
| 28 | 29 | 30 | 31 | 0  | 0  | 0  |

开始: 周三, 周三次数 5 次, 周二次数 4 次, 不满足
| Mo | Tu | We | Th | Fr | Sa | Su |
|----|----|----|----|----|----|----|
| 0  | 0  | 1  | 2  | 3  | 4  | 5  |
| 6  | 7  | 8  | 9  | 10 | 11 | 12 |
| 13 | 14 | 15 | 16 | 17 | 18 | 19 |
| 20 | 21 | 22 | 23 | 24 | 25 | 26 |
| 27 | 28 | 29 | 30 | 31 | 0  | 0  |

开始: 周四, 周三次数 4 次, 周二次数 4 次, 不满足
| Mo | Tu | We | Th | Fr | Sa | Su |
|----|----|----|----|----|----|----|
| 0  | 0  | 0  | 1  | 2  | 3  | 4  |
| 5  | 6  | 7  | 8  | 9  | 10 | 11 |
| 12 | 13 | 14 | 15 | 16 | 17 | 18 |
| 19 | 20 | 21 | 22 | 23 | 24 | 25 |
| 26 | 27 | 28 | 29 | 30 | 31 | 0  |

开始: 周五, 周三次数 4 次, 周二次数 4 次, 不满足
| Mo | Tu | We | Th | Fr | Sa | Su |
|----|----|----|----|----|----|----|
| 0  | 0  | 0  | 0  | 1  | 2  | 3  |
| 4  | 5  | 6  | 7  | 8  | 9  | 10 |
| 11 | 12 | 13 | 14 | 15 | 16 | 17 |
| 18 | 19 | 20 | 21 | 22 | 23 | 24 |
| 25 | 26 | 27 | 28 | 29 | 30 | 31 |

开始: 周六, 周三次数 4 次, 周二次数 4 次, 不满足
| Mo | Tu | We | Th | Fr | Sa | Su |
|----|----|----|----|----|----|----|
| 0  | 0  | 0  | 0  | 0  | 1  | 2  |
| 3  | 4  | 5  | 6  | 7  | 8  | 9  |
| 10 | 11 | 12 | 13 | 14 | 15 | 16 |
| 17 | 18 | 19 | 20 | 21 | 22 | 23 |
| 24 | 25 | 26 | 27 | 28 | 29 | 30 |
| 31 | 0  | 0  | 0  | 0  | 0  | 0  |

开始: 周天, 周三次数 4 次, 周二次数 5 次, 满足
| Mo | Tu | We | Th | Fr | Sa | Su |
|----|----|----|----|----|----|----|
| 0  | 0  | 0  | 0  | 0  | 0  | 1  |
| 2  | 3  | 4  | 5  | 6  | 7  | 8  |
| 9  | 10 | 11 | 12 | 13 | 14 | 15 |
| 16 | 17 | 18 | 19 | 20 | 21 | 22 |
| 23 | 24 | 25 | 26 | 27 | 28 | 29 |
| 30 | 31 | 0  | 0  | 0  | 0  | 0  |

就是这么简单 (bushi).