Apakah ada cara umum untuk 'memperluas' daftar untuk digunakan sebagai argumen individual ke fungsi lain?

9

Misalnya, katakan saya punya daftar string L, mungkin dari &restargumen. Apa yang bisa saya lakukan agar Lefeknya sama dengan yang berikut ini?

(concat (first L) (second L) ... (last L))

(Saya tahu mapconcatakan bekerja di sini untuk contoh ini , tetapi saya sedang mencari proses umum.)

Sean Allred
sumber

Jawaban:

10
(apply #'concat '("foo" "bar" "baz"))
wasamasa
sumber
1
Secara harfiah hanya memikirkan ini! Tautan psikis.
Sean Allred
6

Apa yang ingin Anda lakukan sepertinya melipat atau melepaskan lipatan dari urutan objek dengan tipe yang sama. Sangat menggoda untuk menggunakan applyuntuk tujuan ini, karena dalam banyak kasus memang akan berhasil. Tapi ini bukan alat yang tepat untuk ini, dan inilah alasannya:

  1. applyadalah mekanisme meta-pemrograman, tidak hanya itu, itu juga terlalu umum untuk tugas karena dapat menangani urutan objek dari tipe yang berbeda, tidak harus memanggil fungsi dua argumen. Akibatnya, beberapa kali Anda akan mendapatkan perilaku yang salah, misalnya:

    (apply 'concat "baz" '("foo" "bar"))
     > "bazfoobar"
    

    Namun secara intuitif, Anda akan mengharapkan ketidakcocokan jenis di sini.

  2. Tidak ada cara untuk memastikan applyakan dapat memproses argumen sebanyak yang Anda bisa berikan, itu biasanya batas yang ditentukan oleh implementasi bahasa.

  3. Fungsi yang dipanggil oleh applyakan bisa mendapatkan referensi dari daftar argumen yang diteruskan dengan cara ini. Ini juga tidak jelas, dan dapat menyebabkan kesalahan di kemudian hari:

    (let ((test (list 1 2 3)))
      (cons 
       (apply (lambda (&rest x)
                (prog1 (cl-reduce '+ x) (setcar x 0)))
              test)
       test))
    ;; This behaviour is undefined.  Could end up both ways
    > (6 1 2 3)
    > (6 0 2 3)
    

    Jika daftar argumen disalin, maka Anda membayar harga lebih banyak memori daripada yang dibutuhkan, tetapi jika itu tidak disalin (lulus apa adanya), maka Anda berisiko merusak daftar, jika fungsi yang dipanggil mengubahnya.


Jadi, cara yang lebih baik untuk melakukannya adalah menggunakan cl-reduce. Manfaatnya adalah ia dirancang khusus untuk melakukan tugas-tugas semacam ini.

(cl-reduce 'concat '("foo" "bar" "baz"))
> "foobarbaz"
wvxvw
sumber