Birden çok değişkene aynı değer atanıyor mu?


12

Bazen aynı değeri birden çok değişkene ayarlamam gerekir.

Python'da yapabilirim

f_loc1 = f_loc2 = "/foo/bar"

Ama elisp'te yazıyorum

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

Bunu "/foo/bar"sadece bir kez kullanarak başarmanın bir yolu olup olmadığını merak ediyorum ?


1
Merhaba Chillar, lütfen @tarsius'un cevabını kabul edilen olarak seçebilir misin? Cevabım size ne istediğinizi veren bir çözüm olduğunu gösteriyor, ama dediğim gibi bu tartışmalı yapay mücadeleyi çözen ama pratikte çok kullanışlı olmayan bir tür egzersiz. tarsuis cevabında bu bariz düşünceyi göstermek için çok çaba sarf etti, bu yüzden cevabının "doğru olan" olmayı hak ettiğini düşünüyorum, bu yüzden herkes tekrar mutlu.
Mark Karpov

1
Birden fazla değişkene aynı değeri atama ihtiyacının örtbas etmek için sözdizimsel şeker aramak yerine düzeltilmesi gereken bir tasarım kusurunu gösterdiğini iddia ediyorum :)
lunaryorn

1
@lunaryorn başlangıçta source& targetaynı yola ayarlamak istiyorsanız ve targetdaha sonra değişebilir, sonra nasıl ayarlanır?
ChillarAnand 9:15

Daha fazla kod göster, böylece kullanım durumunuz için "değişken" ile ne demek istediğinizi söyleyebiliriz; yani, ne tür değişkenler ayarlamaya çalışıyorsunuz. @ JordonBiondo'nun cevabı için yorumuma bakın.
Drew

Yanıtlar:


21

setq değeri döndürür, böylece şunları yapabilirsiniz:

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

Buna güvenmek istemiyorsanız, şunu kullanın:

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

Şahsen ben ikincisini önlemek ve bunun yerine yazmak:

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

ya da

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

Ve ilk yaklaşımı sadece niyeti gerçekten vurguladığı ya da alternatifin ikinci yaklaşımı ya da başka bir yöntemi kullanacağı oldukça özel durumlarda kullanacağım progn.


Yukarıdakiler sizi mutlu ederse, burada okumayı bırakabilirsiniz. Ama bunun sizi ve başkalarını makroları kötüye kullanmamaları konusunda uyarmak için iyi bir fırsat olduğunu düşünüyorum.


Son olarak, setq-every"çünkü bu lisp ve yapabiliriz" gibi bir makro yazmak cazip olsa da, buna karşı şiddetle tavsiye ederim. Bunu yaparak çok az kazanıyorsunuz:

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

Ancak aynı zamanda diğer okuyucuların kodu okumasını ve ne olduğundan emin olmanızı zorlaştırıyorsunuz. setq-everyçeşitli şekillerde uygulanabilir ve diğer kullanıcılar (bir gelecek dahil) yazarın hangisini seçtiğini bilemez, sadece onu kullanan koda bakarak. [Aslında burada bazı örnekler verdim, ancak bunlar alaycı ve rant olarak kabul edildi.]

Her baktığınızda bu zihinsel yük ile başa çıkabilir setq-everyveya tek bir ekstra karakterle yaşayabilirsiniz. (En azından sadece iki değişken ayarlamanız gerektiğinde, ancak aynı anda aynı değerde ikiden fazla değişken ayarlamak zorunda olduğumu hatırlayamıyorum. Bunu yapmak zorundaysanız, muhtemelen yanlış bir şey daha vardır kodunuzla.)

Öte yandan, bu makroyu belki yarım düzineden fazla kullanacaksanız, ek dolaylama buna değer olabilir. Mesela ben gibi Makro kullanan --when-letgelen dash.elkütüphanede. Bu, kodumda ilk karşılaşanlar için daha zor hale getirir, ancak anlayıştaki zorluğun üstesinden gelmek buna değer çünkü çünkü sadece birkaç kez daha fazla kullanılır ve en azından benim düşünceme göre, kodu daha okunabilir hale getirir .

Aynı şey için de geçerli olabilir setq-every, ancak bence bu özel makronun birkaç kereden fazla kullanılması pek olası değildir. Başlıca iyi bir kural, makronun tanımı (doc-string dahil) makronun kullanımından daha fazla kod eklediğinde, makronun aşırı bir olasılıkla aşırıya kaçmasıdır (bunun tersi mutlaka doğru değildir).


1
içinde bir var ayarladıktan sonra setqyeni değerini referans alabilirsiniz, bu yüzden bu canavarlığı da kullanabilirsiniz: (setq a 10 b a c a d a e a)abcd ve
e'yi

1
@JordonBiondo, Evet, ama OP amacı kodunda tekrarını azaltmak olduğunu düşünüyorum :-)
Mark Karpov

3
Makro kötüye kullanıma karşı uyarı için +10 vermek istiyorum!
lunaryorn

Sadece makro en iyi uygulama hakkında bir görev oluşturdu. emacs.stackexchange.com/questions/21015 . Hope @tarsius ve diğerleri harika cevaplar verir.
Yasushi Shoji

13

İstediğinizi yapacak bir makro

Bir çeşit egzersiz olarak:

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

Şimdi deneyin:

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

O nasıl çalışır

İnsanlar nasıl çalıştığını merak ettiklerinden (yorumlara göre), işte bir açıklama. Makro yazmayı gerçekten iyi bir Common Lisp kitabı seçmeyi öğrenmek için (evet, Common Lisp, Emacs Lisp'de aynı şeyleri yapabileceksiniz, ancak Common Lisp biraz daha güçlü ve daha iyi kitapları var, IMHO).

Makrolar ham kod üzerinde çalışır. Makrolar argümanlarını değerlendirmez (işlevlerin aksine). Yani burada değerlendirilmedik valueve koleksiyonumuz var vars, ki bunlar bizim makrolarımız için sadece semboller.

prognbirkaç setqformu bir grupta toplar . Bu şey:

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

Sadece setqOP'nin örneğini kullanarak formların bir listesini oluşturur :

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

Gördüğünüz gibi, form backquote formunun içindedir ve bir virgül ile gelir ,. Geri alıntılanan formun içinde her şey genellikle alıntılanır, ancak , geçici olarak “açık” değerlendirme yapılır, bu nedenle tamamı mapcarmakro genişleme zamanında değerlendirilir.

Son olarak @dış parantezleri setqs ile listeden kaldırır , böylece:

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

Makrolar kaynak kodunuzu keyfi olarak dönüştürebilir, harika değil mi?

Bir uyarı

İşte küçük bir uyarı, ilk argüman birkaç kez değerlendirilecek, çünkü bu makro esasen aşağıdakilere genişliyor:

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

Görüyorsunuz, eğer burada bir değişkeniniz veya dizeniz varsa sorun değil, fakat böyle bir şey yazarsanız:

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

Ardından işleviniz birden fazla kez çağrılır. Bu istenmeyen bir durum olabilir. Yardımı once-only( MMT paketinde bulunur ) yardımıyla nasıl düzelteceğiniz aşağıda açıklanmıştır :

(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))))

Ve sorun gitti.


1
Bunun nasıl çalıştığını ve bu kodun daha ayrıntılı olarak ne yaptığını açıklayabilirseniz, bir dahaki sefere insanlar böyle bir şey yazabilirler :)
clemera

valueMakro genişleme zamanında değerlendirmek istemezsiniz .
tarsius

@tarsius, ben: (macroexpand '(setq-every user-emacs-directory f-loc1 f-loc2))-> (progn (setq f-loc1 user-emacs-directory) (setq f-loc2 user-emacs-directory)). Eğer valuemacroexpansion anda değerlendirildi gerçek dize ile ikame edilecektir. Yan etkileri olan fonksiyon çağrısını koyabilirsiniz, yerine, valuegenişletilmiş kod çalıştırılana kadar değerlendirilmez, deneyin. valueçünkü makro argümanı otomatik olarak değerlendirilmez. Asıl mesele, valueistenmeyen olabilecek birkaç kez değerlendirilecek olması, once-onlyburada yardımcı olabilir.
Mark Karpov

1
Bunun yerine mmt-once-only(harici bir dep gerektirir), kullanabilirsiniz macroexp-let2.
YoungFrog

2
Ugh. Soru , bir makroyu setsarmak yerine yinelemeyi kullanmak için bağırıyor setq. Veya yalnızca setqbağımsız değişkenlerin tekrarı da dahil olmak üzere birden çok bağımsız değişkenle kullanın .
Drew

7

Lisp'te sembolleri kullanabildiğiniz ve değiştirebildiğiniz için, bir semboller listesi ve kullanımı üzerinde basitçe döngü yapabilirsiniz 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
Bu,
lexical

@ npostavs: Doğru, ama OP'nin istediği / ihtiyacı bu olabilir. Eğer sözcük değişkenleri kullanmıyorsa, kesinlikle gitmek için bir yoldur. Yine de haklısınız, bu "değişken" belirsizdir, bu yüzden genel olarak soru her türlü değişken anlamına gelebilir. Gerçek kullanım durumu iyi sunulmamıştır, IMO.
Drew
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.