同じ値を複数の変数に割り当てますか?


12

時には、複数の変数に同じ値を設定する必要があります。

Pythonでは、私はできる

f_loc1 = f_loc2 = "/foo/bar"

しかし、elispでは、私は書いています

(setq f_loc1 "/foo/bar" f_loc2 "/foo/bar")

"/foo/bar"一度だけ使用してこれを達成する方法があるかどうか疑問に思っていますか?


1
こんにちは、Chillar、受け入れられたものとして@tarsiusの回答を選択してください。私の答えはあなたが望むものを提供する解決策を示していますが、私が言ったように、それはこの間違いなく人為的な挑戦を解決するが実際にはあまり有用ではない「一種の運動」です。tarsuisは彼の答えでこの明らかな考慮事項を実証するために多大な努力を払ってきたので、彼の答えは「正しいもの」に値すると思います。
マークカルポフ

1
複数の変数に同じ値を割り当てる必要があるということは、カバーするために構文糖を探すのではなく、修正すべき設計上の欠陥を示していると主張します:)
lunaryorn

1
たい場合はセットに@lunaryorn sourcetarget冒頭で同じパスに、私は変更される可能性がありますtargetし、それらを設定する方法を、後から?
ChillarAnand

より多くのコードを表示して、ユースケースの「変数」の意味を理解できるようにします。つまり、設定しようとしている変数の種類。@JordonBiondoの答えについては私のコメントをご覧ください。
ドリュー

回答:


21

setq 値を返すので、次のことができます。

(setq f-loc1 (setq f-loc2 "/foo/bar"))

に依存したくない場合は、次を使用します:

(setq f-loc1 "/foo/bar" f-loc2 f-loc1)

個人的に私は後者を避け、代わりに書くでしょう:

(setq f-loc1 "/foo/bar"
      f-loc2 f-loc1)

あるいは

(setq f-loc1 "/foo/bar")
(setq f-loc2 f-loc1)

そして、最初のアプローチは、実際に意図を強調するような特別な状況でのみ使用するか、代替案が2番目のアプローチを使用するなどの場合にのみ使用しますprogn


上記があなたを幸せにするなら、ここで読むのをやめることができます。しかし、これはあなたや他の人にマクロを乱用しないよう警告する良い機会だと思います。


最後に、setq-every「これはLispであり、できるからです」のようなマクロを作成するのは魅力的ですが、そうしないことを強くお勧めします。あなたはそれをすることでほとんど利益を得ません:

(setq-every value a b)
   vs
(setq a (setq b value))

しかし同時に、他の読者がコードを読んで何が起こるかを確認することをはるかに困難にします。setq-everyさまざまな方法で実装でき、他のユーザー(未来を含む)は、それを使用するコードを見ただけでは、著者がどちらを選択したかを知ることができません。[私はもともとここでいくつかの例を作成しましたが、それらは誰かによって皮肉であり、暴言と見なされていました。]

あなたが見るたびにその精神的なオーバーヘッドに対処することができます、setq-everyまたはあなたはただ1つの余分なキャラクターと一緒に暮らすことができます。(少なくとも2つの変数のみを設定する必要がある場合ですが、一度に3つ以上の変数を同じ値に設定しなければならなかったことを思い出せません。あなたのコードで。)

一方、このマクロを本当に半ダース以上使用するのであれば、追加のインダイレクションは価値があるかもしれません。たとえば--when-letdash.elライブラリのようなマクロを使用します。それは私のコードで最初に遭遇した人にとって難しくなりますが、それはほんの数回以上使用されるため、理解の困難を克服する価値があり、少なくとも私の意見では、コードがより読みやすくなります。

同じことがに当てはまると主張することもできますがsetq-every、この特定のマクロが数回以上使用される可能性は非常に低いと思います。経験則としては、マクロの定義(doc-stringを含む)が、マクロを使用するよりも多くのコードを追加すると、マクロが過剰になりすぎる可能性が高くなります(ただし、逆は必ずしも当てはまりません)。


1
あなたは1つのVARを設定した後にsetq:あなたはこのあまりに怪物使用することができますので、あなたは、それが新しい値だ参照することができます(setq a 10 b a c a d a e a)10にセットがabcdのと電子を
ジョーダンビオンド

1
@JordonBiondo、はい、しかし、私はOPの目的は彼のコードの繰り返しを減らすことだと思います:
マーク・カルポフ

3
マクロの不正使用に対する警告に対して+10を差し上げます。
lunaryorn

マクロのベストプラクティスに関するクエストを作成しました。emacs.stackexchange.com/questions/21015。@tarsiusと他の人が素晴らしい答えを与えることを願っています。
靖商事

13

やりたいことをするマクロ

ある種の演習として:

(defmacro setq-every (value &rest vars)
  "Set every variable from VARS to value VALUE."
  `(progn ,@(mapcar (lambda (x) (list 'setq x value)) vars)))

今すぐ試してください:

(setq-every "/foo/bar" f-loc1 f-loc2)

仕組み

人々は(コメントによると)どのように機能するのか興味があるので、ここに説明があります。マクロの書き方を実際に学ぶには、優れたCommon Lispの本を選んでください(そう、Common Lispは、Emacs Lispでも同じことができますが、Common Lispはもう少し強力で、より良い本があります、IMHO)。

マクロは生のコードで動作します。マクロは引数を評価しません(関数とは異なります)。ですから、ここでは未評価でvalueありvars、マクロのコレクションは単なるシンボルです。

progn複数のsetqフォームを1つにグループ化します。このこと:

(mapcar (lambda (x) (list 'setq x value)) vars)

setqOPの例を使用して、フォームのリストを生成するだけです。

((setq f-loc1 "/foo/bar") (setq f-loc2 "/foo/bar"))

ご覧のとおり、フォームはバッククォート形式の内側にあり、プレフィックスとしてカンマが付いています ,。逆引用符で囲まれたフォーム内では、すべてが通常どおり引用されますが、, 一時的に「mapcar有効化」評価されるため、マクロ展開時に全体が評価されます。

最後にsを使用して@リストから外側の括弧を削除するsetqため、次のようになります。

(progn
  (setq f-loc1 "/foo/bar")
  (setq f-loc2 "/foo/bar"))

マクロはソースコードを任意に変換できますが、素晴らしいことではありませんか?

警告

このマクロは基本的に次のように展開されるため、小さな警告があります。最初の引数は数回評価されます。

(progn
  (setq f-loc1 "/foo/bar")
  (setq f-loc2 "/foo/bar"))

ここに変数または文字列がある場合は問題ありませんが、次のように記述した場合:

(setq-every (my-function-with-side-effects) f-loc1 f-loc2)

次に、関数が複数回呼び出されます。これは望ましくない場合があります。once-onlyMMTパッケージで利用可能)を使用し て修正する方法は次のとおりです。

(defmacro setq-every (value &rest vars)
  "Set every variable from VARS to value VALUE.

VALUE is only evaluated once."
  (mmt-once-only (value)
    `(progn ,@(mapcar (lambda (x) (list 'setq x value)) vars))))

そして、問題はなくなりました。


1
これがどのように機能し、このコードが何をしているのかについての説明を追加できればいいので、次回はこのようなものを自分で書くことができます:)
clemera

valueマクロ展開時に評価する必要はありません。
タルシウス

@tarsius、私はしません:(macroexpand '(setq-every user-emacs-directory f-loc1 f-loc2))-> (progn (setq f-loc1 user-emacs-directory) (setq f-loc2 user-emacs-directory))valueマクロ展開時に評価された場合、実際の文字列に置き換えられます。副作用のある関数呼び出しを置くことができます。代わりにvalue、拡張コードが実行されるまで評価されません。試してください。valueマクロの引数は自動的に評価されません。本当の問題は、value望ましくないonce-onlyかもしれないが、ここで役立つかもしれない数回評価されることです。
マークカルポフ

1
mmt-once-only(外部depが必要)の代わりに、を使用できますmacroexp-let2
YoungFrog

2
あー 質問は、マクロでsetラップするのsetqではなく、との反復を使用するよう叫びます。またはsetq、引数の繰り返しを含む複数の引数で使用します。
ドリュー

7

Lispでシンボルを使用および操作できるため、シンボルのリストを単純にループして使用できますset

(dolist (var '(foo bar baz)) (set var 10))

(mapc (lambda (var) (set var 10)) '(foo bar baz))

(loop for var in '(foo bar baz) do (set var 11))

(--each '(foo bar baz) (set it 10))

2
しかし、これは字句的にバインドされた変数では機能しません
-npostavs

@npostavs:本当ですが、OPが望んでいる/必要としているものかもしれません。字句変数を使用していない場合は、間違いなくその方法です。ただし、「変数」はあいまいであるため、一般的に質問はあらゆる種類の変数を意味します。IMOの実際の使用例は十分に提示されていません。
ドリュー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.