Apa perbedaan antara tanda kurung dan tanda kurung yang dibutuhkan?

Satu hal yang membuat saya sedikit bingung adalah perbedaan antara tanda kurung dan tanda kurung dalam pernyataan memerlukan clojure. Saya ingin tahu apakah seseorang dapat menjelaskan hal ini kepada saya. Misalnya, ini melakukan hal yang sama:

(ns sample.core
  (:gen-class)
  (:require clojure.set clojure.string))

Dan

 (ns sample.core
  (:gen-class)
  (:require [clojure.set] 
            [clojure.string]))

Namun, ini berfungsi dari repl

(require 'clojure.string 'clojure.test)

Tapi gagal di file clj

(ns sample.core
  (:gen-class)
  (:require 'clojure.string 'clojure.test))
...
Exception in thread "main" java.lang.Exception: lib names inside prefix lists must not contain periods
    at clojure.core$load_lib.doInvoke(core.clj:5359)
    at clojure.lang.RestFn.applyTo(RestFn.java:142)
    ....

Padahal hal ini tampaknya melakukan hal yang sama:

(ns sample.core
  (:gen-class)
  (require clojure.set clojure.string))

(ns sample.core
  (:gen-class)
  (:require clojure.set clojure.string))

Secara umum saya tidak memahami hal ini. Saya memahami penggunaan, impor, dan kebutuhan. Tapi saya tidak mengerti ":" dan perbedaan antara hal-hal di [] dan '() dll. Adakah yang bisa menjelaskan topik ini dengan cara yang intuitif?


person David Williams    schedule 09.04.2013    source sumber
comment
kemungkinan duplikat Mengapa memerlukan dalam bentuk ns berperilaku berbeda dari fungsi yang dibutuhkan   -  person om-nom-nom    schedule 09.04.2013
comment
Hm, tidak terlalu menanyakan tentang [] dan perbedaan kode repl dan clj.   -  person David Williams    schedule 09.04.2013


Jawaban (2)


Masalahnya di sini tidak kentara dan mungkin sulit untuk diatasi tanpa terlebih dahulu memahami sedikit tentang makro.

Makro memanipulasi sintaksis dengan cara yang sama seperti fungsi memanipulasi nilai. Faktanya, makro hanyalah fungsi dengan kait yang menyebabkannya dievaluasi pada waktu kompilasi. Mereka meneruskan data literal yang Anda lihat di kode sumber dan dievaluasi dari atas ke bawah. Mari kita buat fungsi dan makro yang memiliki isi yang sama sehingga Anda dapat melihat perbedaannya:

(defmacro print-args-m [& args]
  (print "Your args:")
  (prn args))

(defn print-args-f [& args]
  (print "Your args:")
  (prn args))

(print-args-m (+ 1 2) (str "hello" " sir!"))

; Your args: ((+ 1 2) (str "hello" " sir!"))

(print-args-f (+ 1 2) (str "hello" " sir!"))

; Your args: (3 "hello sir!")

Makro diganti dengan nilai kembaliannya. Anda dapat memeriksa proses ini dengan macroexpand

(defmacro defmap [sym & args]
  `(def ~sym (hash-map ~@args))) ; I won't explain these crazy symbols here.
                                 ; There are plenty of good tutorials around

(macroexpand
  '(defmap people
     "Steve" {:age 53, :gender :male}
     "Agnes" {:age 7,  :gender :female}))

;  (def people
;    (clojure.core/hash-map
;      "Steve" {:age 53, :gender :male}
;      "Agnes" {:age 7, :gender :female}))

Pada titik ini, saya mungkin harus menjelaskan bahwa ' menyebabkan formulir berikut menjadi quoted. Artinya compiler akan membaca form, tapi tidak mengeksekusinya atau mencoba menyelesaikan simbol dan sebagainya. yaitu 'conj mengevaluasi ke suatu simbol, sedangkan conj mengevaluasi ke suatu fungsi. (eval 'conj) setara dengan (eval (quote conj)) setara dengan conj.

Dengan mengingat hal tersebut, ketahuilah bahwa Anda tidak dapat menyelesaikan simbol sebagai namespace sampai simbol tersebut telah diimpor secara ajaib ke dalam namespace Anda. Inilah yang dilakukan fungsi require. Dibutuhkan simbol dan menemukan namespace yang sesuai, membuatnya tersedia di namespace saat ini.

Mari kita lihat apa yang diperluas oleh makro ns:

(macroexpand
  '(ns sample.core
    (:require clojure.set clojure.string)))

;  (do
;    (clojure.core/in-ns 'sample.core)
;    (clojure.core/with-loading-context
;      (clojure.core/refer 'clojure.core)
;      (clojure.core/require 'clojure.set 'clojure.string)))

Lihat bagaimana ia mengutip simbol clojure.set dan clojure.string untuk kita? Alangkah nyaman! Tapi apa masalahnya jika Anda menggunakan require sebagai pengganti :require?

(macroexpand
 '(ns sample.core
   (require clojure.set clojure.string)))

;  (do
;    (clojure.core/in-ns 'sample.core)
;    (clojure.core/with-loading-context
;      (clojure.core/refer 'clojure.core)
;      (clojure.core/require 'clojure.set 'clojure.string)))

Tampaknya siapa pun yang menulis makro ns cukup baik membiarkan kami melakukan keduanya, karena hasil ini persis sama seperti sebelumnya. rapi!

edit: tvachon benar hanya menggunakan :require karena ini adalah satu-satunya formulir yang didukung secara resmi

Tapi apa masalahnya dengan tanda kurung?

(macroexpand
  '(ns sample.core
    (:require [clojure.set] 
              [clojure.string])))

; (do
;  (clojure.core/in-ns 'sample.core)
;  (clojure.core/with-loading-context
;   (clojure.core/refer 'clojure.core)
;   (clojure.core/require '[clojure.set] '[clojure.string])))

Ternyata mereka juga dikutip, sama seperti yang kita lakukan jika kita menulis panggilan mandiri ke require.

Ternyata ns juga tidak peduli apakah kita memberikannya daftar (paren) atau vektor (tanda kurung) untuk dikerjakan. Ia hanya melihat argumen sebagai rangkaian hal. Misalnya, ini berfungsi:

(ns sample.core
  [:gen-class]
  [:require [clojure.set]
            [clojure.string]])

require, seperti yang ditunjukkan oleh amalloy di komentar, memiliki semantik yang berbeda untuk vektor dan daftar, jadi jangan bingung!

Terakhir, mengapa cara berikut ini tidak berhasil?

(ns sample.core
  (:require 'clojure.string 'clojure.test))

Nah, karena ns yang mengutip untuk kita, simbol-simbol ini dikutip dua kali, yang secara semantik berbeda dengan dikutip hanya sekali dan juga murni kegilaan.

conj    ; => #<core$conj clojure.core$conj@d62a05c>
'conj   ; => conj 
''conj  ; => (quote conj)
'''conj ; => (quote (quote conj))

Saya harap ini membantu, dan saya merekomendasikan mempelajari cara menulis makro. Itu sangat menyenangkan.

person d.j.sheldrick    schedule 09.04.2013
comment
(:require (clojure.set) (clojure.string)) tidak berfungsi sama sekali. Ini adalah larangan operasi, yang terlihat berfungsi karena Anda memilih dua namespace yang sudah diperlukan. Cobalah pada beberapa namespace yang tidak ada: berhasil secara diam-diam; pada namespace yang masih ada, ia diam-diam tidak melakukan apa pun. Menggunakan tanda kurung di sini menunjukkan daftar awalan, seperti pada (:require (clojure set string)); sintaks yang Anda berikan hanya berfungsi dengan vektor. - person amalloy; 09.04.2013
comment
jawaban yang bagus, dan +1 untuk referensi TDT, jika memang begitu - person Hendekagon; 10.04.2013
comment
DJ kenapa ini bisa : (ns image-test.core (:gen-class) (:require (png-extract) [clojure.string :as string])) tapi gagal : (ns image-test.core (:gen-class) (:require [png-extract] [clojure.string :as string])) - person David Williams; 20.04.2013

TL;DR:

(ns sample.core
  (:gen-class)
  (:require clojure.set clojure.string))

Dan

 (ns sample.core
  (:gen-class)
  (:require [clojure.set] 
            [clojure.string]))

keduanya baik-baik saja - versi kedua hanyalah kasus khusus dari sintaks paling fleksibel yang didukung require. Ini juga dapat ditulis sebagai:

 (ns sample.core
  (:gen-class)
  (:require [clojure set string]))

Secara umum, bentuk terakhir ini adalah praktik terbaik untuk kebutuhan khusus ini.


(require 'clojure.string 'clojure.test)

Juga berfungsi di file clj - coba ini:

(ns sample.core
  (:gen-class))
(require 'clojure.string 'clojure.test)

Kebingungan di sini adalah bahwa dalam contoh Anda yang rusak, Anda mencoba menggunakan "simbol tanda kutip" di klausa :require pada makro ns. Itu mungkin bukan penjelasan yang paling intuitif, tetapi berikut penjelasannya:

Ada dua cara untuk membutuhkan modul lain, require dan ns.

require adalah fungsi yang mengambil daftar bentuk kutipan ("kutipan" diperlukan untuk menghindari clojure mencari simbol yang Anda teruskan ke require seperti halnya semua simbol lainnya).

ns adalah makro yang mendukung opsi :require. Dibutuhkan nilai opsi ini dan, secara tersembunyi, mengubahnya menjadi panggilan ke fungsi require. Anda tidak perlu mengutip nilai opsi :require karena ns adalah makro dan oleh karena itu mampu mengutip simbol itu sendiri.

Itu mungkin masih belum jelas, tapi saya sarankan untuk membuka dokumentasi Clojure untuk memperjelas - setelah Anda sepenuhnya memahami semuanya, Anda akan memiliki pemahaman yang lebih baik tentang Clojure secara umum.

Dalam file sumber Clojure Anda harus selalu menggunakan klausa ns untuk mewajibkan perpustakaan - require hanya boleh digunakan di REPL.


Dalam dua contoh terakhir Anda benar

(ns sample.core
  (:gen-class)
  (require clojure.set clojure.string))

berhasil, tapi ini kecelakaan - mungkin akibat dari fakta itu

(name :require)
=> "require"

(name 'require)
=> "require"

Sintaks yang didokumentasikan adalah

(ns sample.core
  (:gen-class)
  (:require clojure.set clojure.string))

dan merupakan satu-satunya yang dijamin tidak akan pecah dikemudian hari.

person tvachon    schedule 09.04.2013
comment
Dalam file sumber Clojure Anda harus selalu menggunakan klausa ns untuk mewajibkan pustaka - require hanya boleh digunakan di REPL. Mengapa? - person Daniel Kaplan; 09.04.2013
comment
Tidak ada alasan teknis - sepenuhnya karena gaya dan keterbacaan. Menggunakan ns secara konsisten memastikan programmer lain dapat dengan mudah melihat namespace apa yang diperlukan oleh suatu file tanpa menggali keseluruhan file. Ini juga sedikit lebih bersih, karena Anda tidak perlu keluar dari formulir secara manual. - person tvachon; 09.04.2013