Archive for the ‘context’ Tag

projecteuler5...または、和集合を定義する

 projecteuler問題 5 は、1 から 20 の整数で割り切れる最小の値を求めるもの。ちなみに、1 から 20 の場合は 2520
 順当に割り算で割り切れるかどうか調べる方法だと、

(let (max-num 20 res 2)
  (for (i 2 max-num)
    (let (factors (factor i))
      (while (> (% res i) 0)
        (setq res (* res (pop factors -1))))))
  res)

 こんな感じ。ここで、% は余りを返す組込の整数演算子、同じく組込の浮動小数点数用の mod を使っても、同じ結果 232792560 になります。
 やっていることは、2 から 20 までの整数で res(初期値 2 )を割り、余りが 0 でないなら、組込関数 factor で素因数分解した値を大きい方から順に res に掛け算して、余りが 0 になるまで繰り返すもの。
 ちなみに newLISP では、整数演算に +, -, *, /, % を、浮動小数点数演算に addsubmuldivmod を使います。
 もちろん、+, -, *, /, % に浮動小数点数演算を割り当てることも簡単にできますが、この整数演算には桁数に制限がないというとんでもない利点があります(笑)。projecteuler13 で紹介した時は、開発版の newLISP で可能でしたが、今では標準装備(V.10.5.0)です。
 これでお終いですが、もう一つ解答例を、

(define (list-or2 lst1 lst2)
  (let (tmp lst1)
    (dolist (x lst2)
      (let (i (find x tmp))
        (if i (pop tmp i) (push x lst1 -1)))))
 lst1)

 と二つのリスト要素の和集合を返す関数list-or2 を定義しておきます。こうすれば、後は 2 から 20 までの整数を素因数分解したリストの集合和を取って、そのリスト要素を全て掛け算するだけ。

> (apply * (apply list-or2 (map factor (sequence 2 20)) 2))
232792560
> 

 こんな感じ。newLISP の組込apply はオプションで畳み込みができますからね。こういう小技も newLISP の魅力の一つ。
 和集合は、組込関数difference を使って、

(define (list-or2 lst1 lst2)
  (let (x (append (difference lst2 lst1 true) lst1)
        y (append (difference lst1 lst2 true) lst2))
    (if (> (length x) (length y)) x y)))

 という風にも書けます。組込関数difference はリスト要素の差を返しますが方向性を持っているので、どちらを返すかリストの長さで決めています。
 さらに和集合を汎用関数として、

(context 'MAIN:list-or)
(define (or2 lst1 lst2)
  (let (tmp (or lst1 '()))
    (dolist (x (or lst2 '()))
      (let (i (and tmp (find x tmp)))
        (if i (pop tmp i) (push x lst1 -1)))))
 lst1)
(define (list-or:list-or)
  (let (len (length (args)))
    (if (= len 1) (args 0)
        (= len 2) (or2 (args 0) (args 1))
        (> len 2) (list-or (args 0) (apply list-or (1 (args))))
        '())))
(context MAIN)

 と定義すれば、

> (apply * (apply list-or (map factor (sequence 2 10))))
2520
> (apply * (apply list-or (map factor (sequence 2 20))))
232792560
> 

 畳み込みもいらない!この方が context を使っていて newLISP っぽい(笑)。

 以上、如何でしょうか?

newLISP の context について考える。。。その2

;; pass data by context reference

(set 'Mydb:data (sequence 1 100000))

(define (change-db obj idx value)
    (setf (obj:data idx) value))

(change-db Mydb 1234 "abcdefg")

(nth 1234 Mydb:data)   → "abcdefg"
; or
(Mydb:data 1234)   → "abcdefg"

 これは、マニュアル“Passing data by reference”項の最後の例題です。
 私は、これで何故、関数change-db が動くのか、判りませんでした。
 ちなみに、macro.lsp を使って、macro を定義している場合、関数change-db は定義できません。定義できないだけで、動作はします(笑)。つまり、関数change-db を定義してから、macro を定義しても、関数change-db は動作するということです。v10.2.15 以降であれば、macro を定義した後でも、関数change-db を定義できます。詳しくは、こちらでもどうぞ

 閑話休題。関数change-db の引数 obj は、コンテキスト名です。
 でも、関数change-db 内で使われている変数は、obj:data 、コンテキスト名が先付されたシンボルのように見えます。
 そして、関数の動作からすると、obj:data のうち、obj は、変数で data は、変数ではなく、シンボル名です。
 newLISPer には、当たり前?
 さて、マニュアルで : の項を見ると、

演算子として使う時、コロン : は、オブジェクト・リストのコンテキスト名からくるコンテキスト・シンボルとコロンに続くシンボルを組み立てます。
When used as an operator, the colon : constructs a context symbol from the context name in the object list and the symbol following the colon.

 とあり、さらに、

コロンとそれに続くシンボルの間は、スペースを要求されません。
No space is required between the colon and the symbol following it.

 No space is required がクセモノでした。要求されないけれど、在ってもよいようです。
 その例が、やはり、マニュアルにありました

(:area myrect) → 200 ; same as (: area myrect)
(:area mycircle) → 314.1592654 ; same as (: area mycircle)

 なるほどね。関数change-db は、

(define (change-db obj idx value)
    (setf (obj: data idx) value))

 とも書けるのですね。
 : の前は、必ずコンテキストなので評価されます。コンテキスト名そのものなら、コンテキストは、それ自身に評価され、コンテキスト名が現れます。変数なら、ここでは、引数に割り当てられたコンテキスト(つまり、コンテキスト名)が入るわけです。
 一方、: の後は、シンボルです。変数にすると、変数名シンボルがそのまま、使われます。
 だから、<a href="“>例題のあったマニュアル項にあるように、

不利な点は、呼び出す関数が使われるシンボルを知っていなければならいないことです:
The disadvantage is that the calling function must have knowledge about the symbol being used:

 と、なる訳です。
 だから、マニュアルは読んでおかないとね(笑)。

 以上、如何でしょうか?

newLISP の context について考える

 突然ですが、次のコードの間違いはどこでしょう?

(context 'FOO)
(set 'ABC 123)
(context MAIN)

(context 'ABC)
(set 'FOO 456)
(context 'MAIN)

 答えの前に、正しいコードを

(context 'FOO)
(set 'ABC 123)
(context MAIN)

(context 'ABC)
(set 'FOO:ABC 456)
(context 'MAIN)

 これは、newLISP のマニュアルにある

コンテキストを生成あるいはロードするシーケンスは、予期しない結果につながることがあります。
The sequence in which contexts are created or loaded can lead to unexpected results.

 に続く例題です。
 予期しないと書いてありますが、マニュアル の Symbol creation in contexts に、

コード翻訳中に、見知らぬシンボルに出会うと、その定義の検索が現在のコンテキスト内で始まります。それに失敗すると、検索は、MAIN 内で組込関数、コンテキスト、グローバル変数について続けられます。定義が見つからなければ、シンボルは、現在のコンテキスト内にローカルに生成されます。
When an unknown symbol is encountered during code translation, a search for its definition begins inside the current context. Failing that, the search continues inside MAIN for a built-in function, context, or global symbol. If no definition is found, the symbol is created locally inside the current context.

 とあります。だから、

ABC のロードの時は、FOO がグローバル・プロテクト・シンボルとして、すでに存在していて、正常にプロテクトのフラグがセットされています。
When ABC loads, FOO already exists as a global protected symbol and will be correctly flagged as protected.

 となるわけです。
 だから、マニュアルは読んでおかないとね(笑)。

 以上、如何でしょうか?