ความแตกต่างระหว่างวงเล็บและวงเล็บใน need คืออะไร?

สิ่งหนึ่งที่ฉันสับสนเล็กน้อยคือความแตกต่างระหว่างวงเล็บและวงเล็บใน clojure ต้องใช้คำสั่ง ฉันสงสัยว่ามีใครสามารถอธิบายเรื่องนี้ให้ฉันฟังได้ไหม ตัวอย่างเช่น สิ่งเหล่านี้ทำสิ่งเดียวกัน:

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

และ

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

อย่างไรก็ตาม สิ่งนี้ใช้ได้จากตัวแทน

(require 'clojure.string 'clojure.test)

แต่ล้มเหลวในไฟล์ 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)
    ....

โดยที่สิ่งเหล่านี้ดูเหมือนจะทำสิ่งเดียวกัน:

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

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

โดยทั่วไปฉันไม่เข้าใจสิ่งนี้ ฉันเข้าใจการใช้ นำเข้า และต้องการ แต่ฉันไม่เข้าใจ quot; และความแตกต่างระหว่างสิ่งต่าง ๆ ใน [] และ '() ฯลฯ ใครสามารถให้ความกระจ่างหัวข้อนี้ด้วยวิธีสัญชาตญาณได้หรือไม่


person David Williams    schedule 09.04.2013    source แหล่งที่มา
comment
เป็นไปได้ที่ซ้ำกันของ เหตุใดจึงต้องมี ในรูปแบบ ns มีพฤติกรรมแตกต่างจากฟังก์ชัน need   -  person om-nom-nom    schedule 09.04.2013
comment
หืมไม่ได้ถามเกี่ยวกับ [] และความแตกต่างระหว่างรหัส repl และ clj   -  person David Williams    schedule 09.04.2013


คำตอบ (2)


ปัญหาที่นี่เป็นเรื่องละเอียดอ่อนและอาจเป็นเรื่องยากหากปราศจากความเข้าใจเกี่ยวกับมาโครสักเล็กน้อยก่อน

แมโครจัดการไวยากรณ์ในลักษณะเดียวกับที่ฟังก์ชันจัดการค่า ที่จริงแล้ว มาโครเป็นเพียงฟังก์ชันที่มีจุดเชื่อมต่อซึ่งทำให้มีการประเมินในเวลาคอมไพล์ ข้อมูลเหล่านี้จะถูกส่งผ่านข้อมูลตามตัวอักษรที่คุณเห็นในซอร์สโค้ดและได้รับการประเมินจากบนลงล่าง มาสร้างฟังก์ชันและมาโครที่มีเนื้อหาเหมือนกันเพื่อให้คุณเห็นความแตกต่าง:

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

แมโครจะถูกแทนที่ด้วยค่าที่ส่งคืน คุณสามารถตรวจสอบกระบวนการนี้ได้ด้วย 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}))

ณ จุดนี้ ฉันควรจะอธิบายว่า ' ทำให้แบบฟอร์มต่อไปนี้เป็น quoted ซึ่งหมายความว่าคอมไพเลอร์จะอ่านแบบฟอร์ม แต่จะไม่ดำเนินการหรือพยายามแก้ไขสัญลักษณ์และอื่นๆ เช่น 'conj ประเมินเป็นสัญลักษณ์ ในขณะที่ conj ประเมินเป็นฟังก์ชัน (eval 'conj) เทียบเท่ากับ (eval (quote conj)) เทียบเท่ากับ conj

ด้วยเหตุนี้ โปรดทราบว่าคุณไม่สามารถแก้ไขสัญลักษณ์เป็นเนมสเปซได้จนกว่าจะถูกนำเข้าสู่เนมสเปซของคุณอย่างน่าอัศจรรย์ นี่คือสิ่งที่ฟังก์ชัน require ทำ ใช้สัญลักษณ์และค้นหาเนมสเปซที่ตรงกัน ทำให้สามารถใช้ได้ในเนมสเปซปัจจุบัน

มาดูกันว่ามาโคร 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)))

ดูว่ามันอ้างอิงสัญลักษณ์ clojure.set และ clojure.string สำหรับเราอย่างไร วิธีที่สะดวก! แต่จะมีประโยชน์อะไรเมื่อคุณใช้ require แทน :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)))

ดูเหมือนว่าใครก็ตามที่เขียนมาโคร ns ก็ยินดีพอที่จะให้เราทำทั้งสองวิธี เนื่องจากผลลัพธ์นี้ยังคงเหมือนเดิมทุกประการ นีโต้!

แก้ไข: tvachon ถูกต้องเกี่ยวกับการใช้ :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])))

ปรากฎว่าพวกเขาได้รับการยกมาเหมือนกัน เช่นเดียวกับที่เราทำถ้าเราเขียนการโทรแบบสแตนด์อโลนไปที่ require

ปรากฎว่า ns ไม่สนใจว่าเราจะใส่รายการ (พาเรนต์) หรือเวกเตอร์ (วงเล็บ) ให้มันใช้งานหรือไม่ มันแค่มองว่าข้อโต้แย้งเป็นลำดับของสิ่งต่าง ๆ ตัวอย่างเช่น วิธีนี้ได้ผล:

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

require ตามที่ amalloy ชี้ให้เห็นในความคิดเห็น มีความหมายที่แตกต่างกันสำหรับเวกเตอร์และรายการ ดังนั้นอย่าปะปนกัน!

สุดท้ายแล้วเหตุใดสิ่งต่อไปนี้จึงใช้งานไม่ได้

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

เนื่องจาก ns ทำการยกมาให้เรา สัญลักษณ์เหล่านี้จึงถูกยกมาสองครั้ง ซึ่งแตกต่างทางความหมายจากการยกมาเพียงครั้งเดียวและยังเป็นความบ้าคลั่งอีกด้วย

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

ฉันหวังว่านี่จะช่วยได้ และฉันขอแนะนำให้เรียนรู้วิธีเขียนมาโครอย่างแน่นอน พวกเขาสนุกสุด ๆ

person d.j.sheldrick    schedule 09.04.2013
comment
(:require (clojure.set) (clojure.string)) ไม่ทำงานเลย เป็นสิ่งที่ไม่ต้องดำเนินการ ซึ่ง ดู ดูเหมือนว่าจะใช้งานได้เนื่องจากคุณเลือกเนมสเปซสองอันซึ่งจำเป็นอยู่แล้ว ลองใช้เนมสเปซที่ไม่มีอยู่จริง: สำเร็จแบบเงียบๆ บนเนมสเปซที่มีอยู่จะไม่ทำอะไรเลย การใช้เครื่องหมายวงเล็บที่นี่แสดงถึงรายการคำนำหน้า เช่นเดียวกับใน (:require (clojure set string)); ไวยากรณ์ที่คุณให้ใช้งานได้กับเวกเตอร์เท่านั้น - person amalloy; 09.04.2013
comment
คำตอบที่ยอดเยี่ยมและ +1 สำหรับการอ้างอิง TDT หากเป็นเช่นนั้น - person Hendekagon; 10.04.2013
comment
ดีเจ ทำไมสิ่งนี้ถึงใช้งานได้: (ns image-test.core (:gen-class) (:require (png-extract) [clojure.string :as string])) แต่สิ่งนี้ล้มเหลว: (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))

และ

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

ทั้งคู่ใช้ได้ - เวอร์ชันที่สองเป็นเพียงกรณีพิเศษของไวยากรณ์ที่ยืดหยุ่นที่สุด require รองรับ นอกจากนี้ยังสามารถเขียนเป็น:

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

โดยทั่วไป แบบฟอร์มสุดท้ายนี้เป็นแนวปฏิบัติที่ดีที่สุดสำหรับความต้องการเฉพาะนี้


(require 'clojure.string 'clojure.test)

ใช้งานได้ในไฟล์ clj ด้วย - ลองสิ่งนี้:

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

ความสับสนที่นี่คือในตัวอย่างที่เสียหายของคุณ คุณกำลังพยายามใช้ "สัญลักษณ์เครื่องหมายคำพูด" ในส่วนคำสั่ง :require ของมาโคร ns นั่นอาจไม่ใช่คำอธิบายที่เข้าใจง่ายที่สุด แต่รายละเอียดมีดังต่อไปนี้:

มีสองวิธีในการขอโมดูลอื่น require และ ns

require เป็นฟังก์ชันที่รับรายการรูปแบบเครื่องหมายคำพูด (จำเป็นต้องใช้ "เครื่องหมายคำพูด" เพื่อหลีกเลี่ยงไม่ให้ clojure ค้นหาสัญลักษณ์ที่คุณส่งไปยัง require เหมือนกับที่มันทำกับสัญลักษณ์อื่นๆ ทั้งหมด)

ns คือมาโครที่รองรับตัวเลือก :require ใช้ค่าของตัวเลือกนี้และแปลงเป็นการเรียกฟังก์ชัน require ภายใต้ฝาครอบ คุณไม่จำเป็นต้องเสนอราคาของตัวเลือก :require เนื่องจาก ns เป็นมาโคร จึงสามารถอ้างอิงถึงสัญลักษณ์ได้

นั่นอาจจะยังไม่ชัดเจน แต่ฉันขอแนะนำให้หันไปดูเอกสารของ Clojure เพื่อชี้แจง - เมื่อคุณเข้าใจทุกสิ่งอย่างถ่องแท้แล้ว คุณจะมีความเข้าใจเกี่ยวกับ Clojure โดยทั่วไปดีขึ้นมาก

ในไฟล์ต้นฉบับ Clojure คุณควรใช้ส่วนคำสั่ง ns เพื่อกำหนดให้มีไลบรารี - require ควรใช้ใน REPL เท่านั้น


ในสองตัวอย่างสุดท้ายของคุณ คุณถูกต้องแล้ว

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

ใช้งานได้ แต่นี่เป็นอุบัติเหตุ - อาจเป็นผลมาจากข้อเท็จจริงนั้น

(name :require)
=> "require"

(name 'require)
=> "require"

ไวยากรณ์ที่บันทึกไว้คือ

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

และเป็นสิ่งเดียวที่รับประกันว่าจะไม่พังในอนาคต

person tvachon    schedule 09.04.2013
comment
ในไฟล์ต้นฉบับ Clojure คุณควรใช้ ns clause เสมอเพื่อต้องการไลบรารี - ควรใช้ need ใน REPL เท่านั้น ทำไม - person Daniel Kaplan; 09.04.2013
comment
ไม่มีเหตุผลทางเทคนิค - มีสไตล์และอ่านง่ายโดยสิ้นเชิง การใช้ ns อย่างสม่ำเสมอทำให้โปรแกรมเมอร์คนอื่นๆ สามารถดูเนมสเปซที่ต้องการในไฟล์ได้อย่างง่ายดาย โดยไม่ต้องขุดค้นทั้งไฟล์ นอกจากนี้ยังสะอาดกว่าเล็กน้อย เนื่องจากคุณไม่จำเป็นต้อง Escape แบบฟอร์มด้วยตนเอง - person tvachon; 09.04.2013