4

無制限の数の引数を取る関数を作成し、それらをリストとして扱うことができるのが好きです。二分木を作成するときに便利で、現在、最近傍アルゴリズムのバリエーションとして使用しています。しかし、私の方法は本当にひどいものです。不適切なリスト (不適切で縮退している可能性があります) を反復処理する方法が思いつかないので、さまざまなリスト関数を使用して、不適切なリストを強制的にリスト形式にしようとしました。

これは、マップノード間の違いを判断するための単純な関数での私の最善の試みです(機能しますが、なぜ機能するのかわかりません):

(define distance-between
  (lambda xs
    (let ([input-list (list* xs null)])
      (letrec 
          ([f (lambda (xs acc)
               (if (null? (cdr xs))
                   acc
                   (f (cdr xs) 
                      (+ (abs (- (map-node-x (car xs)) 
                                 (map-node-x (cadr xs))))
                         (abs (- (map-node-y (car xs)) 
                                 (map-node-y (cadr xs))))
                         acc))))])                   
       (f (car input-list) 0)))))

ご覧のとおり、これは醜い解決策であり、私には魔法のように見えるもののいくつかが含まれています.不適切なリストをリストに含めると、リスト形式に強制されるのはなぜですか*? (注: この文は誤解を招きます。これは発生しません)。

私はむしろきれいな解決策があり、魔法はありません。誰でも助けることができますか?

たとえば、典型的な入力は次のようになります。

(distance-between (map-node 1 2) (map-node 2 3) (map-node 3 4))

期待される結果で:

4

(map-node (a) と mn (b) の間の距離 2、さらに map-node (b) と map-node (c) の間の距離 2)。

あるいは、次のように単純に入力することもできます:

(distance-between (map-node 1 2) (map-node 2 2))

そして次の答えを得る:

1

(let ([input-list...])...) ステートメントを使用せずに生の入力でこれを試みた場合、(? 実際にはなぜこの質問に応答したのかわかりません) としてエラーが発生します。

機能は期待どおりに機能します。

4

3 に答える 3

4

また、可変引数リストがどのように不適切であるかもわかりません。

しかし、元の質問に答えるために (不適切な可能性のあるリストを反復処理する方法を、ややエレガントに)、​​次を使用する 1 つの方法を示しmatchます。

#lang racket

(define (properly-sum-improper-list xs)
  (let loop ([acc 0]
             [xs xs])
    (match xs
      [(list) acc]
      [(cons x more) (loop (+ acc x) more)]
      [x (+ acc x)]))) ;last item of improper list

(require rackunit)
(check-equal? (properly-sum-improper-list '(1 2 3 4))   10)
(check-equal? (properly-sum-improper-list '(1 2 3 . 4)) 10)

ただし、これを行う必要があるということは、おそらく、他の何かを修正または変更する必要があることを示しています。

于 2013-08-13T15:02:19.370 に答える
3

あなたのリストは不適切ではありません。引数がペアでない場合、(lambda xs body ...)または(define (fun . xs) body ...)すべての引数がリストに丸呑みされます。たとえば(fun 1 2 3)、 xs を作成し'(1 2 3)ます。ループ(list* '(1 2 3) '())を呼び出すことで'((1 2 3)、すぐに元に戻すことができます。car'(1 2 3)

それ以外は、手順は意図したとおりに機能します。手順を少し整理するかもしれませんが、次の 2 つの要素を折りたたむリストを滑空するリスト内包表記がないため、それほど小さくはなりません。以下は基本的に同じコードですが、(letrec+call のシンタックス シュガーである) イテレータ ループとして aを使用して作業を行う手順(存在する場合は foldl-pair を使用できます) を抽象化しています。named let

(define (distance-between e1 . lst)
  (define (add-diff-acc e1 e2 acc)
    (+ (abs (- (map-node-x e1) (map-node-x e2)))
       (abs (- (map-node-y e1) (map-node-y e2)))
       acc))

  (let iterate ((e1 e1) (lst lst) (acc 0))
    (if (pair? lst)
        (let ((e2 (car lst)))
          (iterate e2 (cdr lst) (add-diff-acc e1 e2 acc)))
        acc)))

EDIT:構文シュガーについて、named letおよびletrec.

(let ((x 10) (y 19)) 
  body)

匿名手続き呼び出しの構文糖衣

((lambda (x y) 
    body) 
  10 19)

Anamed letはそのプロシージャに名前を付けているだけですが、あたかも byのように、再帰バインディングletrecを作成しています。指定した名前で呼び出すと、引数は let の初期値ではなく、指定したものになります。私はそれらに慣れていて、今日はそれらを好みます。慣れるまで時間がかかるかもしれませんが。

私たちが書くコードのほとんどは、いくつかの低レベルのもののためのシンタックス シュガーです。マクロはネストされているため、letrecフォーム最終的にラムダに縮小されます。シンタックス シュガーを使用しない手順全体は次のようになります。

(define distance-between 
  (lambda (e1 . lst)
    ((lambda (add-diff-acc)
       ((lambda (iterate e1 lst acc) ; emulate Y to substitute `letrec`
          (iterate iterate e1 lst acc))
        (lambda (iterate e1 lst acc)
          (if (pair? lst)
              ((lambda (e2)
                 (iterate iterate e2 (cdr lst) (add-diff-acc e1 e2 acc)))
               (car lst))
              acc))
        e1 lst 0))
     (lambda (e1 e2 acc)
       (+ (abs (- (map-node-x e1) (map-node-x e2)))
          (abs (- (map-node-y e1) (map-node-y e2)))
          acc)))))
于 2013-08-13T22:50:43.387 に答える