Hatena::Groupcadr

わだばLisperになる このページをアンテナに追加 RSSフィード

2004 | 12 |
2005 | 01 | 02 | 07 | 10 | 11 |
2006 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2007 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2008 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2009 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2010 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2011 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 11 |

2008-07-02

サンプルコードによるLOOPマクロ入門 (5)

| 15:07 | サンプルコードによるLOOPマクロ入門 (5) - わだばLisperになる を含むブックマーク はてなブックマーク - サンプルコードによるLOOPマクロ入門 (5) - わだばLisperになる

値の走査/生成

集める方法を先に紹介してしまいましたが、値を走査/生成する方法にも色々あります。

大まかに分けると、数値を生成するものと、リスト/ベクタを走査するものです。

リスト

:inと:onが使えます。:inは、各要素を順番に、:onは、デフォルトで順にcdrを取得します。

(loop :for i :in '(1 2 3 4 5) :collect i)
;=> (1 2 3 4 5)

(loop :for i :on '(1 2 3 4 5) :collect i)
;=> ((1 2 3 4 5) (2 3 4 5) (3 4 5) (4 5) (5))
  • 取得する間隔を変更する

:byの後に関数を指定することで間隔を変更することができます。

デフォルトでは、#'cdrなので、1つおきにしたい場合、#'cddrという風になります。

なんとなく、:inで、#'cddrという指定は直感的でない気もします。

(loop :for i :in '(1 2 3 4 5) :by #'cddr :collect i)
;=> (1 3 5)

(loop :for i :on '(1 2 3 4 5) :by #'cddr :collect i)
;=> ((1 2 3 4 5) (3 4 5) (5))
ベクタ
(loop :for i :across #(1 2 3 4 5) :collect i)
;=> (1 2 3 4 5)

(loop :for i :across "1-2-3-4-5" 
      :when (parse-integer (string i) :junk-allowed 'T) 
        :collect :it)
;=> (1 2 3 4 5)

ベクタの場合は、:inや、:onではなくて、別の:acrossというキーワードを使います。

一々型によって使い分けるのも面倒な気もしますが、マクロ展開時に、型に応じてコードを出力するためらしいので型が変れば違う指定をする位に考えておくと良いかもしれません。とはいえ文字列もベクタなので同じキーワードです。

数値
(loop :for i :from 0 :to 10 :collect i)
;=> (0 1 2 3 4 5 6 7 8 9 10)

(loop :for i :from 0 :to 10 :by 2 :collect i)
;=> (0 2 4 6 8 10)

(loop :for i :from 0 :below 10 :collect i)
;=> (0 1 2 3 4 5 6 7 8 9)

(loop :for i :from 10 :downto 0 :collect i)
;=> (10 9 8 7 6 5 4 3 2 1 0)

(loop :for i :from 10 :above 0 :collect i)
;=> (10 9 8 7 6 5 4 3 2 1)

数値の生成には、:fromや、:toのように範囲を指定すれば、順に増加/減少した数値を取得できます。これも:byによってステップを変更できます。

:from、:downfromが開始の指定、:to、:upto、:downto、:below、:above等色々あって一々面倒ですが、これもマクロ展開ため(キーワードで方向を明示しないと増加方向なのか、減少方向なのか推測できない)ためらしいので、そういうものだと思って暗記するしかないでしょう。

(loop :repeat 10 :for i :from 0 :collect i)
;=> (0 1 2 3 4 5 6 7 8 9)

(loop :repeat 10 :for i :downfrom 100 :by 3 :collect i)
;=> (100 97 94 91 88 85 82 79 76 73)

のように、:repeatと組み合わせれば、終点の指定を省略できます。XからYステップでN個欲しいというような場合には、こっちの方が分かりやすいかもしれません。