หากคุณสามารถใช้ def เพื่อกำหนดตัวแปรวิธีที่ถือว่าไม่เปลี่ยนรูปได้อย่างไร


10

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

เพื่อยกตัวอย่างว่าเป็นอย่างไร

a = 1
a = 2

ใน Ruby (หรือร้องไห้สะอึกสะอื้นหากคุณต้องการ) แตกต่างจาก

(def a 1)
(def a 2)

ใน Clojure

คำตอบ:


9

ดังที่คุณได้สังเกตเห็นแล้วความจริงที่ว่าความไม่แน่นอนนั้นเกิดขึ้นใน Clojure นั้นไม่ได้หมายความว่าเป็นสิ่งต้องห้ามและไม่มีสิ่งก่อสร้างที่รองรับ ดังนั้นคุณถูกต้องที่ใช้defคุณสามารถเปลี่ยน / กลายพันธุ์ผูกพันในสภาพแวดล้อมในลักษณะที่คล้ายกับสิ่งที่ได้รับมอบหมายในภาษาอื่น ๆ (ดูเอกสาร Clojure บน vars ) ด้วยการเปลี่ยนการเชื่อมในสภาพแวดล้อมโกลบอลคุณจะเปลี่ยนวัตถุข้อมูลที่ใช้การเชื่อมเหล่านี้ ตัวอย่างเช่น:

user=> (def x 1)
#'user/x
user=> (defn f [y] (+ x y))
#'user/f
user=> (f 1)
2
user=> (def x 100)
#'user/x
user=> (f 1)
101

โปรดสังเกตว่าหลังจากนิยามการโยงxใหม่อีกครั้งฟังก์ชั่นfก็เปลี่ยนไปเช่นกันเพราะร่างกายใช้การโยงนั้น

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

- val x = 1;
val x = 1 : int
- fun f y = x + y;
val f = fn : int -> int
- f 1;
val it = 2 : int
- val x = 100;
val x = 100 : int
- f 1;
val it = 2 : int

แจ้งให้ทราบว่าหลังจากที่ความหมายที่สองของxฟังก์ชั่นfยังคงใช้ผูกพันx = 1ที่อยู่ในขอบเขตเมื่อมันถูกกำหนดไว้คือผูกพันไม่ได้เขียนทับก่อนหน้านี้มีผลผูกพันval x = 100val x = 1

Bottomline: Clojure อนุญาตให้เปลี่ยนแปลงสภาพแวดล้อมทั่วโลกและกำหนดการผูกใหม่ในนั้น มันเป็นไปได้ที่จะหลีกเลี่ยงสิ่งนี้เช่นเดียวกับภาษาอื่น ๆ เช่น SML แต่defโครงสร้างใน Clojure นั้นมีจุดประสงค์เพื่อเข้าถึงและเปลี่ยนแปลงสภาพแวดล้อมของโลก ในทางปฏิบัติสิ่งนี้คล้ายกับสิ่งที่ได้รับมอบหมายสามารถทำได้ในภาษาที่จำเป็นเช่น Java, C ++, Python

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


1
การหลีกเลี่ยงการกลายพันธุ์นั้นเป็นรูปแบบการเขียนโปรแกรมที่ต้องการ ฉันขอแนะนำว่าข้อความนี้ใช้กับทุกภาษาในทุกวันนี้ ไม่ใช่แค่ Clojure;)
David Arno

2

Clojure เป็นข้อมูลเกี่ยวกับที่ไม่เปลี่ยนรูป

Clojure เกี่ยวกับการจัดการสถานะที่ไม่แน่นอนโดยการควบคุมคะแนนการกลายพันธุ์ (เช่นRefs, Atoms, Agents, และVars) อย่างไรก็ตามแน่นอนว่าโค้ด Java ใด ๆ ที่คุณใช้ผ่านทาง interop สามารถทำได้ตามที่ต้องการ

แต่คุณสามารถกำหนดตัวแปรใหม่ได้อย่างง่ายดายโดยใช้ def ใช่ไหม?

หากคุณหมายถึงการผูก a Var(ตรงข้ามกับเช่นตัวแปรท้องถิ่น) กับค่าที่แตกต่างกันแล้วใช่ ในความเป็นจริงตามที่ระบุไว้ในVars และสภาพแวดล้อมทั่วโลก , Vars ถูกรวมไว้เป็นหนึ่งใน "ประเภทอ้างอิง" หนึ่งในสี่ของ Clojure (แม้ว่าฉันจะบอกว่าพวกเขาส่วนใหญ่หมายถึงแบบไดนามิก Varที่นั่น)

ด้วย Lisps มีประวัติอันยาวนานของการทำกิจกรรมการเขียนโปรแกรมเชิงโต้ตอบและเชิงสำรวจผ่าน REPL สิ่งนี้มักจะเกี่ยวข้องกับการกำหนดตัวแปรและฟังก์ชั่นใหม่เช่นเดียวกับการกำหนดตัวแปรเก่า อย่างไรก็ตามนอกเหนือจาก REPL defการอ้างอิงVarเป็นรูปแบบที่ไม่ดี


1

จากClojure เพื่อความกล้าหาญและความจริง

ตัวอย่างเช่นใน Ruby คุณอาจทำการมอบหมายหลายครั้งให้กับตัวแปรเพื่อสร้างมูลค่า:

severity = :mild
  error_message = "OH GOD! IT'S A DISASTER! WE'RE "
  if severity == :mild
    error_message = error_message + "MILDLY INCONVENIENCED!"
  else
    error_message = error_message + "DOOOOOOOMED!"
  end

คุณอาจถูกล่อลวงให้ทำสิ่งที่คล้ายกันใน Clojure:

(def severity :mild)
  (def error-message "OH GOD! IT'S A DISASTER! WE'RE ")
  (if (= severity :mild)
      (def error-message (str error-message "MILDLY INCONVENIENCED!"))
  (def error-message (str error-message "DOOOOOOOMED!")))

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

(defn error-message [severity]
   (str "OH GOD! IT'S A DISASTER! WE'RE "
   (if (= severity :mild)
     "MILDLY INCONVENIENCED!"
     "DOOOOOOOMED!")))

(error-message :mild)
  ; => "OH GOD! IT'S A DISASTER! WE'RE MILDLY INCONVENIENCED!"

คนเราไม่สามารถทำสิ่งเดียวกันในทับทิมได้อย่างง่ายดายหรือ ข้อเสนอแนะที่ได้รับเพียงเพื่อกำหนดฟังก์ชั่นที่ส่งกลับค่า Ruby มีฟังก์ชั่นด้วยเช่นกัน!
Evan Zamir

ใช่ฉันรู้. แต่แทนที่จะสนับสนุนวิธีที่จำเป็นในการแก้ปัญหาที่เสนอ (เช่นการเปลี่ยนแปลงการผูก) Clojure ยอมรับกระบวนทัศน์เกี่ยวกับการทำงาน
Tiago Dall'Oca
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.