Skip to content

Instantly share code, notes, and snippets.

@kohyama
Last active October 19, 2017 08:55
Show Gist options
  • Save kohyama/5857345 to your computer and use it in GitHub Desktop.
Save kohyama/5857345 to your computer and use it in GitHub Desktop.
文字列を先頭から見て同じところまで除去 in Clojure
(require '[clojure.test :refer (with-test are run-tests)])
(with-test
(defn str-drop-while-same [& args]
(if (some nil? args) args
(->> (map #(concat % (repeat nil)) args)
(apply map vector)
(drop-while #(apply = %))
(take-while #(some identity %))
(apply map str))))
(are [args expected]
(= (apply str-drop-while-same args) expected)
["abcdef" "abc123"] ["def" "123"]
["あいうえお" "あいさんさん" "あいどる"] ["うえお" "さんさん" "どる"]
["12345" "67890" "12abc"] ["12345" "67890" "12abc"]
["a" "ab" "abc"] ["" "b" "bc"]
[nil "a" "ab"] [nil "a" "ab"]
["" "a" "ab"] ["" "a" "ab"]))
@kohyama
Copy link
Author

kohyama commented Jun 25, 2013

えまのんわん さんの
「文字列を先頭から見て同じところまで除去」を Clojure で解く というエントリを読んだので, 自分でもやってみました.
お題はここ から来たようです.

テストは えまのんわん さんのものをそのまま使わせていただいています.

以下解説です.

             ; args が ("abcdef" "abc1234") だとして例示します.
(if (some nil? args) args
             ; 与えられた文字列の中に nil があれば元のまま返し, そうでなければ以下を返します.
             ; 例では nil が含まれないので, 以下を返します.
    (->> (map #(concat % (repeat nil)) args)
             ; (repeat nil) は (nil nil nil ...) という無限シーケンスです.
             ; これを元の文字列(が自動で文字のシーケンスに変換されたもの)の全てに concat します.
             ; 例では
             ;   ((\a \b \c \d \e \f nil nil nil ...)
             ;    (\a \b \c \1 \2 \3 \4 nil nil nil ...))
             ; のようになります.
        (apply map vector)
             ; 転置します.
             ; 無限シーケンスです.
             ; 例では
             ;   ([\a \a] [\b \b] [\c \c] [\d \1] [\e \2] [\f \3] [nil \4] [nil nil] [nil nil] ...)
             ; のようになります.
        (drop-while #(apply = %))
             ; 全ての要素が等しい間は捨てます. #(apply = %) は (partial apply =) でも良いです.
             ; まだ無限シーケンスです.
             ; 例では
             ;   ([\d \1] [\e \2] [\f \3] [nil \4] [nil nil] [nil nil] ...)
             ; のようになります.
        (take-while #(some identity %))
             ; 先頭から nil でない値がひとつ以上含まれている間は採用し, その後は捨てます.
             ; ここで有限長になります.
             ; #(some identity %) は (partial some identity) でも良いです.
             ; 例では
             ;   ([\d \1] [\e \2] [\f \3] [nil \4])
             ; となります.
        (apply map str))
             ; 各番目の要素の並びを str で文字列化します.
             ; 例では
             ;   ((str \d \e \f nil) (str \1 \2 \3 \4))
             ; すなわち
             ;   ("def" "1234")
             ; になります.

->> マクロは, 直前の式の評価結果を, 次の式の最後の要素として挿入します.
(->> (a0 a1) (b0 b1) (c0 c1) (d0 d1))(d0 d1 (c0 c1 (b0 b1 (a0 a1))))と同等です.

user=> (macroexpand-1 '(->> (a0 a1) (b0 b1) (c0 c1) (d0 d1)))
(d0 d1 (c0 c1 (b0 b1 (a0 a1))))

REPL では *1 で直前の評価結果を参照できるので, 各式の評価過程を例で見ることができます.

user=> (set! *print-length* 10)
10
user=> '("abcdef" "abc1234")
("abcdef" "abc1234")
user=> (map #(concat % (repeat nil)) *1)
((\a \b \c \d \e \f nil nil nil nil ...) (\a \b \c \1 \2 \3 \4 nil nil nil ...))
user=> (apply map vector *1)
([\a \a] [\b \b] [\c \c] [\d \1] [\e \2] [\f \3] [nil \4] [nil nil] [nil nil] [nil nil] ...)
user=> (drop-while #(apply = %) *1)
([\d \1] [\e \2] [\f \3] [nil \4] [nil nil] [nil nil] [nil nil] [nil nil] [nil nil] [nil nil] ...)
user=> (take-while #(some identity %) *1)
([\d \1] [\e \2] [\f \3] [nil \4])
user=> (apply map str *1)
("def" "1234")

デフォルトでは無限シーケンスを評価すると, 全て印字しようとして評価から帰って来ないので,
(set! *print-length* 10) で, シーケンスを印字する際の要素数を 10 に制限しています.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment