2012-01-22 9 views
15

誰かが次のような行動を説明することはできますか?具体的には、関数は毎回異なるリストを返すのはなぜですか?関数が呼び出されるたびにsome-list'(0 0 0)に初期化されないのはなぜですか?この関数は毎回異なる値を返すのはなぜですか?

(defun foo() 
    (let ((some-list '(0 0 0))) 
    (incf (car some-list)) 
    some-list)) 

出力:

> (foo) 
(1 0 0) 
> (foo) 
(2 0 0) 
> (foo) 
(3 0 0) 
> (foo) 
(4 0 0) 

ありがとう!

EDIT:

また、出力'(1 0 0)に私は機能を望むたびに仮定して、この機能を実装するための推奨方法は何ですか?

答えて

21

'(0 0 0)は(変更から保護されていないが)一定であると仮定されているオブジェクトリテラルです。したがって、毎回同じオブジェクトを効果的に変更しています。各関数呼び出しで異なるオブジェクトを作成するには、(list 0 0 0)を使用します。

あなたが何をしているのかわからない限り、リテラルリスト('(0 0 0)など)は常に定数として使用する必要があります。サイドノートでは

+0

ああ、今は意味をなさない。明確な説明をありがとう。 –

+2

おそらく、クォータクォーテイングが新鮮なリストを返すことが保証されていないということを追加するといいでしょう。 – 6502

+3

"わかっていない限り、あなたがやっていること"リテラルデータを変更する動作は未定義です。仕様によると、実際にあなたが何をしているのかを(確実に)知ることができないので、 "**常に定数としてのみ( '(0 0 0)のような)リテラルリストを使うべきです。 –

-5

一つを自分で書きたかったが、私は良い1つのオンラインを見つけた:

CommonLispは、ファーストクラスの機能を持っている、すなわち機能が は、実行時に作成され、他の関数に引数として渡すことができるオブジェクトです。 - AlainPicardこれらのファーストクラスの関数も独自の状態を持つため、ファンクタです。すべてのLisp関数はファンクタです。 "just code"と "function objects"の間には、 という区切りがありません。状態は、キャプチャされた字句変数 バインディングの形式をとります。バインディングを取得するためにLAMBDAを使用する必要はありません。 トップレベルのDEFUNは、あまりにもそれを行うことができます:(((プライベート変数42)) (defunはfooの() ...)しましょう)

...の代わりに、コードは、プライベート変数見てその語彙 スコープでこの変数のインスタンスは、 に関連付けられ、シンボルFOOにグローバルに関連付けられた関数オブジェクトのみが関連付けられます。 変数は、DEFUN式の評価時に取得されます。 この変数は、Cで静的変数のように動作します。 代わりに、FOOを 「インスタンス変数」を持つ「シングルトン」オブジェクトと考えることができます。

--KazKylheku

文献http://c2.com/cgi/wiki?CommonLisp

+1

引用したテキストが質問にどのように関連しているか説明できますか?私は何かを見逃しているかもしれないが、私はそれを見ない。 – sepp2k

+0

このテキストは、関数がどのようにしてLispのファーストクラスのオブジェクトであるかを説明し、実際には "状態"を持っています。宣言された変数は、関数の "状態"の一部でした。テキストが説明しているように、これはCで静的なローカル変数を宣言するのと非常によく似ています。テキストのどの部分がこの問題に関係していませんか? – xtrem

+2

それはまったく起こっていない部分です。あなたの見積もりは、「キャプチャされたレキシカル変数のバインディング」について語ります。しかし、 'some-list'は' foo'のローカル変数です。キャプチャされた変数ではなく、 'foo'の状態の一部ではありません。 'foo'を呼び出すたびに、' some-list'は一意のバインディングを持ちます(Vsevolodは、OPの挙動を説明する同じ "定数"リストを指しています)。これは、キャプチャされた変数を変更する関数とはまったく異なります。 – sepp2k

9

、次の警告を取得SBCL REPLでこの関数を定義する:

caught WARNING: 
    Destructive function SB-KERNEL:%RPLACA called on constant data. 
    See also: 
     The ANSI Standard, Special Operator QUOTE 
     The ANSI Standard, Section 3.2.2.3 

当面の問題に向けて良いヒントを与えます。

4

'(0 0 0)は、リテラルデータです。このデータを変更すると、未定義の動作が発生します。 Common Lispの実装は、実行時にそれを検出しないかもしれません(例えば、データがいくつかの読み出し専用メモリ空間に置かれていない限り)。しかし、それは望ましくない影響を及ぼすことがあります。同じ機能より微妙な可能性のあるエラーの

  • 1の様々な呼び出し間で共有しますが、このデータがあること(そして多くの場合である)ことを見

    • このです:Common Lispはさまざまな最適化で定義されていますこれはコンパイラを念頭に置いて行うことができます。上記のコードで

      (let ((a '(1 2 3)) 
           (b '(1 2 3))) 
          (list a b)) 
      

      コンパイラはabのリテラルデータがEQUALであることを検出することができるスニペット:

    例:例えば、コンパイラは、データを再利用することができます。次に、両方の変数が同じリテラルデータを指している可能性があります。変更することはできますが、変更はabから表示されます。

    要約:リテラルデータの変更は、いくつかの微妙なバグの原因です。可能な場合は避けてください。次に、のコン新しいデータオブジェクトが必要です。 コンシンクは一般に、実行時に新しく新しいデータ構造を割り当てることを意味します。

  • 関連する問題