2017-02-05 3 views
0

リスト内のアトムの位置を探しようとしています。アトムの位置を見つける - 存在しない場合はnilを返します。

期待される結果:

(position-in-list 'a '(a b c d e))が0

(position-in-list 'b '(a b c d e))を与える1

(position-in-list 'Z '(a b c d e))がnilを与える与えます。

私は項目がリストにあるときに正しく位置を与える機能を持っている:

(defun position-in-list (letter list) 
    (cond 
    ((atom list)   nil) 
    ((eq (car list) letter) 0) 
    (t      (+ 1 (position-in-list letter (cdr list)))))) 

それは(atom list)に到達したかのような問題は、項目が存在しない場合にはnilを返さないことですnilはこのエラーを返します:*** - 1+: nil is not a number as unacksとき、それはnilに値を追加しようとします。

アイテムがリストにないときに正しくnilを返すように(同じ構造を保って)この関数を適合させる方法はありますか?

注:

  • 私は図書館でposition機能があることを知っているが、私はそれを使用する必要はありません。

  • 私の質問はthis oneと似ていますが、上記で言及した問題は解決されていません。あなたの答えのためのあなたのすべてに

*編集* 感謝。あなたが言及したすべての提案を理解するのに必要な知識はありませんが、それは参考になりました。

私は私の問題への別の修正を発見した:この実装は末尾再帰ではないことに注意してください

(defun position-in-list (letter list) 
    (cond 
    ((atom list)   nil) 
    ((eq (car list) letter) 0) 
    (t 
    (let ((found (position-in-list letter (cdr list)))) 
     (and found 
      (1+ found)))))) 

(defun position-in-list (letter liste) 
    (cond 
     ((atom liste) nil) 
     ((equal letter (car liste)) 0) 
     ((position-in-list letter (cdr liste)) (+ 1 (position-in-list letter (cdr liste)))))) 
+0

これはあいまいで非効率的です。あなたが勉強しようとしているので、私はあなたがckearestを探して、最も効率的であることを期待します。 (これは既にsdsによって提供されていますが、 – kennytilton

答えて

4

あなたは再帰呼び出しによって返された値をチェックする必要があります。

5

可能な解決策の1つは、再帰関数を別の関数のローカル関数にすることです。最後にはから復帰し、周囲の関数から戻ります。したがって、各再帰呼び出しからNILの結果を返す必要はありません。

ローカル再帰関数はLABELSと定義することができる機能から

ローカル再帰関数が戻ります。関数はローカル関数から見えるから復帰するため

(defun position-in-list (letter list) 
    (labels ((position-in-list-aux (letter list) 
      (cond 
       ((atom list)    (return-from position-in-list nil)) 
       ((eql (first list) letter) 0) 
       (t       (+ 1 (position-in-list-aux 
               letter (cdr list))))))) 
    (position-in-list-aux letter list))) 

これRETURN-FROMが可能です。

再帰関数は、他の機能に戻る

CATCHTHROW使用して他の関数に制御を戻すことも可能です。また、その

(defun position-in-list (letter list) 
    (catch 'position-in-list-catch-tag 
    (position-in-list-aux letter list))) 

(defun position-in-list-aux (letter list) 
    (cond 
    ((atom list)    (throw 'position-in-list-catch-tag nil)) 
    ((eql (first list) letter) 0) 
    (t       (+ 1 (position-in-list-aux 
            letter (cdr list)))))) 

テスト機能EQL

注慣例による既定のテスト機能はEQLで、ではありません。これにより、数字と文字も使用できます。

3

一般に、使用する平等関数を選択するには、:testキーワードパラメータを指定すると便利です。コンパイラにtail-call-optimize機能を持たせることも便利です(Common LispではTCOは必要ありませんが、ほとんどのコンパイラは適切な最適化設定を行い、コンパイラのマニュアルを参照します)ので、別のキーワードパラメータを使用しますそれのために。また、最も内側の呼び出しから返されたものはそのまま返されるので、数字を返すかどうかは関係ありません。つまりnilです。もちろん

(defun position-in-list (element list &key (test #'eql) (position 0)) 
    (cond ((null list) nil) 
     ((funcall test element (car list)) position) 
     (t (position-in-list element 
           (cdr list) 
           :test test :position (1+ position))))) 

、内部機能でTCOフレンドリー再帰をラップするために、おそらく優れているので、我々は(ライナーJOSWIGが正しく指摘するように)内部実装の詳細が公開されていません。

(defun position-in-list (element list &key (test #'eql) 
    (labels ((internal (list position) 
       (cond ((null list) nil) 
        ((eql element (car list)) position) 
        (t (internal (cdr list) (1+ position)))))) 
    (internals list 0))) 
+0

エラーを修正したいかもしれません::key - > list、:postion - > position –

+0

スタイル:そのような内部変数を公開するのは、キーワード引数を提供する場合、呼び出し元がそれを使用して何か有益なことを行うことができるというのが通常の予想です。 –

関連する問題