מבוא לקלוז'ר

1. הקדמה

Clojure היא שפת תכנות פונקציונלית הפועלת כולה במכונה הווירטואלית של Java, באופן דומה לסקאלה ולקוטלין. Clojure נחשב לנגזרת Lisp ויהיה מוכר לכל מי שיש לו ניסיון בשפות Lisp אחרות.

מדריך זה נותן מבוא לשפת Clojure, המציג כיצד להתחיל איתו וכמה ממושגי המפתח כיצד היא פועלת.

2. התקנת Clojure

Clojure זמין כמתקינים וסקריפטים נוחים לשימוש ב- Linux ו- MacOS. למרבה הצער, בשלב זה, ל- Windows אין מתקין כזה.

עם זאת, סקריפטים של לינוקס עשויים לעבוד במשהו כמו Cygwin או Windows Bash. יש גם שירות מקוון שניתן להשתמש בו לבדיקת השפה, ולגרסאות ישנות יותר יש גרסה עצמאית בה ניתן להשתמש.

2.1. הורדה עצמאית

ניתן להוריד את קובץ ה- JAR העצמאי ממייבן סנטרל. למרבה הצער, גרסאות חדשות מ- 1.8.0 כבר לא עובדות בצורה כזו בקלות בגלל שקובץ JAR חולק למודולים קטנים יותר.

לאחר הורדת קובץ JAR זה, אנו יכולים להשתמש בו כ- REPL אינטראקטיבי פשוט על ידי התייחסות אליו כאל JAR להפעלה:

$ java -jar clojure-1.8.0.jar Clojure 1.8.0 user =>

2.2. ממשק אינטרנט ל- REPL

ממשק אינטרנט ל- Clojure REPL זמין בכתובת //repl.it/languages/clojure כדי שנוכל לנסות ללא צורך להוריד שום דבר. נכון לעכשיו, זה תומך רק ב- Clojure 1.8.0 ולא במהדורות החדשות יותר.

2.3. מתקין ב- MacOS

אם אתה משתמש ב- MacOS והתקנת Homebrew, ניתן להתקין את המהדורה האחרונה של Clojure בקלות:

$ brew להתקין clojure

זה יתמוך בגרסה האחרונה של Clojure - 1.10.0 בזמן כתיבת שורות אלה. לאחר ההתקנה, אנו יכולים לטעון את ה- REPL פשוט באמצעות קלוז'ר אוֹ clj פקודות:

$ clj Clojure 1.10.0 user =>

2.4. מתקין בלינוקס

ניתן להתקין סקריפט פגז להתקנה עצמית להתקנת הכלים בלינוקס:

$ curl -O //download.clojure.org/install/linux-install-1.10.0.411.sh $ chmod + x linux-install-1.10.0.411.sh $ sudo ./linux-install-1.10.0.411.sh

כמו במתקין ה- macOS, אלה יהיו זמינים למהדורות האחרונות של Clojure וניתן לבצע אותם באמצעות קלוז'ר אוֹ clj פקודות.

3. מבוא ל- Clojure REPL

כל האפשרויות שלעיל נותנות לנו גישה ל- Clojure REPL. זהו המקבילה הישירה ל- Clojure לכלי JShell עבור Java 9 ומעלה ומאפשר לנו להזין קוד Clojure ולראות את התוצאה באופן מיידי ישירות. זוהי דרך נהדרת להתנסות ולגלות כיצד תכונות שפה מסוימות עובדות.

לאחר טעינת ה- REPL, נקבל הנחיה שבה ניתן להזין ולבצע את כל קוד ה- Clojure הסטנדרטי. זה כולל קונסטרוקציות פשוטות של Clojure, כמו גם אינטראקציה עם ספריות Java אחרות - אם כי הן צריכות להיות זמינות בכיתה כדי לטעון.

ההנחיה של ה- REPL היא אינדיקציה למרחב השמות הנוכחי בו אנו עובדים. לרוב העבודה שלנו, זהו מִשׁתַמֵשׁ מרחב השמות, וכך ההנחיה תהיה:

משתמש =>

כל השאר בהמשך מאמר זה יניח שיש לנו גישה ל- Clojure REPL, וכולם יעבדו ישירות בכל כלי כזה.

4. יסודות השפה

שפת Clojure נראית שונה מאוד משפות רבות אחרות מבוססות JVM, וייתכן שהיא נראית יוצאת דופן מאוד מלכתחילה. זה נחשב לדיאלקט של Lisp ויש לו תחביר ופונקציונליות דומים מאוד לשפות אחרות של Lisp.

הרבה מהקודים שאנו כותבים ב- Clojure - כמו עם ניבים אחרים של Lisp - מתבטאים בצורה של רשימות.. לאחר מכן ניתן להעריך רשימות להפקת תוצאות - בצורה של רשימות נוספות או ערכים פשוטים.

לדוגמה:

(+ 1 2) ; = 3

זו רשימה המורכבת משלושה אלמנטים. סמל "+" מציין שאנחנו מבצעים שיחה זו - תוספת. לאחר מכן משתמשים באלמנטים הנותרים בשיחה זו. לפיכך, הערכה זו היא "1 + 2".

באמצעות תחביר רשימה כאן, ניתן להרחיב זאת באופן טריוויאלי. לדוגמא, אנו יכולים לעשות:

(+ 1 2 3 4 5) ; = 15

וזה מוערך ל "1 + 2 + 3 + 4 + 5".

שימו לב גם לדמות חצי המעי הגס. זה משמש ב- Clojure כדי לציין הערה ואינו סוף הביטוי כפי שהיינו רואים ב- Java.

4.1. פָּשׁוּט סוגים

Clojure בנוי על גבי ה- JVM, וככזה יש לנו גישה לאותם סוגים סטנדרטיים כמו כל יישום Java אחר. סוגים מוספרים בדרך כלל באופן אוטומטי ואין צורך לציין אותם במפורש.

לדוגמה:

123; ארוך 1.23; כפול "שלום"; מחרוזת נכון; בוליאני

אנו יכולים לציין גם כמה סוגים מסובכים יותר באמצעות קידומות או סיומות מיוחדות:

42N; clojure.lang.BigInt 3.14159M; java.math.BigDecimal 1/3; clojure.lang.Ratio # "[A-Za-z] +"; java.util.regex.Pattern

שים לב שה- clojure.lang.BigInt סוג משמש במקום java.math.BigInteger. הסיבה לכך היא שלסוג Clojure יש כמה אופטימיזציות ותיקונים קלים.

4.2. מילות מפתח וסמלים

Clojure נותן לנו את המושג הן מילות מפתח והן סמלים. מילות מפתח מתייחסות רק לעצמן ומשמשות לעתים קרובות לדברים כגון מקשי מפה. לעומת זאת, סמלים הם שמות המשמשים להתייחס לדברים אחרים. לדוגמא, הגדרות משתנות ושמות פונקציות הם סמלים.

אנו יכולים לבנות מילות מפתח באמצעות שם שקודם לו נקודתיים:

user =>: kw: kw user =>: a: a

למילות מפתח יש שוויון ישיר עם עצמן ולא עם שום דבר אחר:

user => (=: a: a) user true => (=: a: b) user false => (=: a "a") false

רוב הדברים האחרים בקלוז'ר שאינם ערכים פשוטים נחשבים לסמלים. אלה מעריכים את כל מה שהם מתייחסים אליוואילו מילת מפתח תמיד מעריכה את עצמה:

user => (def a 1) # 'user / a user =>: a: a user => a 1

4.3. מרחבי שמות

לשפת Clojure יש את המושג מרחבי שמות לארגון הקוד שלנו. כל פיסת קוד שאנו כותבים חיה במרחב שמות.

כברירת מחדל, ה- REPL פועל ב- מִשׁתַמֵשׁ מרחב שמות - כפי שנראה על ידי ההנחיה המציינת "user =>".

אנו יכולים ליצור ולשנות מרחבי שמות באמצעות ה- ns מילת מפתח:

משתמש => (ns new.ns) אפס new.ns =>

לאחר ששינינו מרחבי שמות, כל מה שמוגדר בזה הישן כבר אינו זמין עבורנו, וכל מה שמוגדר בזה החדש זמין כעת.

אנו יכולים לגשת להגדרות בכל מרחבי שמות על ידי התאמתם המלאה. למשל, מרחב השמות מחרוזת מגדיר פונקציה אותיות גדולות.

אם אנחנו ב מחרוזת אנו יכולים לגשת אליו ישירות. אם לא, עלינו לסייג את זה כ- מחרוזת / מיתר עליון:

משתמש => (clojure.string / אותיות גדולות "שלום") "שלום" משתמש => (אותיות גדולות "שלום"); זה לא נראה במרחב השמות "משתמש" תחביר שגיאת קומפילציה ב (REPL: 1: 1). לא ניתן לפתור את הסמל: אותיות רישיות בהקשר זה user => (ns clojure.string) nil clojure.string => (במקרה העליון "שלום"); זה גלוי מכיוון שאנו נמצאים כעת במרחב השמות "clojure.string" "HELLO"

אנחנו יכולים גם להשתמש ב- לִדרוֹשׁמילת מפתח כדי לגשת להגדרות ממרחב שמות אחר בצורה קלה יותר. ישנן שתי דרכים עיקריות בהן אנו יכולים להשתמש - להגדיר מרחב שמות בשם קצר יותר כך שיהיה קל יותר לשימוש, וכדי לגשת להגדרות ממרחב שמות אחר ללא קידומת ישירה:

clojure.string => (דרוש '[clojure.string: as str]) אפס clojure.string => (str / במקרה העליון "שלום") "HELLO" משתמש => (דורש' [clojure.string: as str: עיין [אותיות גדולות]]) משתמש אפס => (אותיות גדולות "שלום") "שלום"

שני אלה משפיעים רק על מרחב השמות הנוכחי, ולכן יהיה צורך לשנות אותו למרחב אחר דורש. זה עוזר לשמור על מרחבי השמות שלנו נקיים יותר ולתת לנו גישה רק למה שאנחנו צריכים.

4.4. משתנים

ברגע שנדע להגדיר ערכים פשוטים, נוכל להקצות אותם למשתנים. אנו יכולים לעשות זאת באמצעות מילת המפתח def:

user => (def a 123) # 'user / a

לאחר שעשינו זאת, נוכל להשתמש בסמל אבכל מקום שאנחנו רוצים לייצג את הערך הזה:

משתמש => 123

הגדרות משתנות יכולות להיות פשוטות או מסובכות כמו שאנחנו רוצים.

לדוגמה, כדי להגדיר משתנה כסכום המספרים, אנו יכולים לעשות:

משתמש => (def b (+ 1 2 3 4 5)) משתמש '/ משתמש b = = b 15

שימו לב שלעולם לא נצטרך להכריז על המשתנה או לציין איזה סוג זה. Clojure קובע את כל זה באופן אוטומטי עבורנו.

אם ננסה להשתמש במשתנה שלא הוגדר, במקום זאת נקבל שגיאה:

user => שגיאת תחביר לא ידועה בקומפילציה ב- (REPL: 0: 0). לא ניתן לפתור את הסמל: לא ידוע בהקשר זה משתמש => (def c (+ 1 לא ידוע)) שגיאת תחביר בקומפילציה ב- (REPL: 1: 8). לא ניתן לפתור את הסמל: לא ידוע בהקשר זה

שימו לב שהפלט של ה- def הפונקציה נראית מעט שונה מהקלט. הגדרת משתנה א מחזירה מחרוזת של 'משתמש / א. הסיבה לכך היא שהתוצאה היא סמל, וסמל זה מוגדר במרחב השמות הנוכחי.

4.5. פונקציות

ראינו כבר כמה דוגמאות כיצד להתקשר לפונקציות ב- Clojure. אנו יוצרים רשימה שמתחילה בפונקציה להיקרא ואז כל הפרמטרים.

כאשר רשימה זו מעריכה, אנו מקבלים את ערך ההחזר מהפונקציה. לדוגמה:

user => (java.time.Instant / now) #object [java.time.Instant 0x4b6690c0 "2019-01-15T07: 54: 01.516Z"] user => (java.time.Instant / parse "2019-01- 15T07: 55: 00Z ") #object [java.time. אינסטנט 0x6b8d96d9" 15/01/2019T07: 55: 00Z "] user => (java.time.OffsetDateTime / of 2019 01 15 7 56 0 0 java.time. ZoneOffset / UTC) #object [java.time.OffsetDateTime 0xf80945f "2019-01-15T07: 56Z"]

אנו יכולים גם לקנן קריאות לפונקציות, כאשר אנו רוצים להעביר את הפלט של קריאת פונקציה אחת כפרמטר לאחר:

user => (java.time.OffsetDateTime / of 2018 01 15 7 57 0 0 (java.time.ZoneOffset / ofHours -5)) #object [java.time.OffsetDateTime 0x1cdc4c27 "2018-01-15T07: 57-05: 00 "]

גַם, אנו יכולים גם להגדיר את הפונקציות שלנו אם אנו רוצים. פונקציות נוצרות באמצעות fn פקודה:

user => (fn [a b] (println "הוספת מספרים" a "ו-" b) (+ a b)) #object [user $ eval165 $ fn__166 0x5644dc81 "[מוגן בדוא"ל]"]

לצערי, זה לא נותן לפונקציה שם שניתן להשתמש בו. במקום זאת, אנו יכולים להגדיר סמל המייצג פונקציה זו באמצעות def, בדיוק כפי שראינו לגבי משתנים:

user => (def add (fn [a b] (println "הוספת מספרים" a "ו-" b) (+ a b))) # 'user / add

כעת, לאחר שהגדרנו פונקציה זו, אנו יכולים לקרוא לה זהה לכל פונקציה אחרת:

משתמש => (הוסף 1 2) הוספת מספרים 1 ו -2 3

לנוחות, Clojure גם מאפשר לנו להשתמש defn להגדרת פונקציה עם שם בבת אחת.

לדוגמה:

user => (defn sub [a b] (println "Subtracting" b "from" a) (- a b)) # 'user / sub user => (sub 5 2) Subtracting 2 from 5 3

4.6. תנו ומשתנים מקומיים

ה def call מגדיר סמל שהוא גלובלי למרחב השמות הנוכחי. זה בדרך כלל לא מה שרוצים בעת ביצוע קוד. במקום זאת, קלוז'ר מציע את לתת התקשר להגדרת משתנים מקומיים לחסימה. זה שימושי במיוחד כשמשתמשים בהם בתוך פונקציות, שם אתה לא רוצה שהמשתנים ידלפו מחוץ לפונקציה.

לדוגמה, נוכל להגדיר את פונקציית המשנה שלנו:

משתמש => (defn sub [a b] (def result (- a b)) (println "Result:" result) result) # 'user / sub

עם זאת, לשימוש בזה יש תופעת לוואי בלתי צפויה הבאה:

משתמש => (משנה 1 2) תוצאה: -1 -1 משתמש => תוצאה; עדיין נראה מחוץ לפונקציה -1

במקום זאת, בואו נכתוב אותו מחדש באמצעות לתת:

user => (defn sub [ab] (let [result (- ab)] (println "Result:" result) result)) # 'user / sub user => (sub 1 2) תוצאה: -1-1 user = > תוצאה של שגיאת תחביר בקומפילציה ב- (REPL: 0: 0). לא ניתן לפתור את הסמל: התוצאה בהקשר זה

הפעם תוֹצָאָה הסמל אינו נראה מחוץ לפונקציה. או, אכן, מחוץ ל לתת בלוק בו הוא שימש.

5. אוספים

עד כה היינו מתקשרים בעיקר עם ערכים פשוטים. ראינו גם רשימות, אך לא יותר מכך. ל- Clojure יש סט מלא של אוספים שניתן להשתמש בהם, אם כי, המורכב מרשימות, וקטורים, מפות וסטים:

  • וקטור הוא רשימת ערכים מסודרת - ניתן להכניס כל ערך שרירותי לווקטור, כולל אוספים אחרים.
  • סט הוא אוסף ערכים בלתי מסודר, ולעולם אינו יכול להכיל את אותו ערך יותר מפעם אחת.
  • מפה היא קבוצה פשוטה של ​​זוגות מפתח / ערך. מקובל מאוד להשתמש במילות מפתח כמפתחות במפה, אך אנו יכולים להשתמש בכל ערך שנרצה, כולל אוספים אחרים.
  • רשימה דומה מאוד לווקטור. ההבדל דומה לזה שבין רשימת מערך ו רשימה מקושרת בג'אווה. בדרך כלל, מועדף וקטור, אך רשימה טובה יותר אם אנו רוצים להוסיף אלמנטים להתחלה, או אם אנו רוצים רק לגשת לאלמנטים בסדר רציף.

5.1. בניית אוספים

יצירת כל אחד מאלה יכולה להיעשות באמצעות סימון קצרצר או באמצעות שיחת פונקציה:

; משתמש וקטורי => [1 2 3] [1 2 3] משתמש => (וקטור 1 2 3) [1 2 3]; משתמש ברשימה => '(1 2 3) (1 2 3) משתמש => (רשימה 1 2 3) (1 2 3); הגדר משתמש => # {1 2 3} # {1 3 2} משתמש => (ערכת hash 1 2 3) # {1 3 2}; משתמש משתמש => {: a 1: b 2} {: a 1,: b 2} user => (hash-map: a 1: b 2) {: b 2,: a 1}

שימו לב שה- מַעֲרֶכֶת ו מַפָּה דוגמאות אינן מחזירות את הערכים באותו סדר. הסיבה לכך היא שאוספים אלה אינם מסודרים מטבעם, ומה שאנו רואים תלוי באופן ייצוגם בזיכרון.

אנו יכולים גם לראות כי התחביר ליצירת רשימה דומה מאוד לתחביר Clojure הסטנדרטי לביטויים. ביטוי של Clojure הוא, למעשה, רשימה שמוערכתואילו אופי האפרופורה כאן מצביע על כך שאנחנו רוצים את רשימת הערכים בפועל במקום להעריך אותה.

אנו יכולים, כמובן, להקצות אוסף למשתנה באותו אופן כמו כל ערך אחר. אנו יכולים גם להשתמש באוסף אחד כמפתח או ערך בתוך אוסף אחר.

רשימות נחשבות א משך. המשמעות היא שהכיתה מיישמת את ISeq מִמְשָׁק. ניתן להמיר את כל האוספים האחרים ל- משך משתמש ב משך פוּנקצִיָה:

user => (seq [1 2 3]) (1 2 3) user => (seq # {1 2 3}) (1 3 2) user => (seq {: a 1 2 3}) ([: a 1] [2 3])

5.2. גישה לאוספים

ברגע שיש לנו אוסף, אנחנו יכולים לקיים איתו אינטראקציה כדי להחזיר את הערכים שוב. איך נוכל לעשות זאת תלוי מעט באוסף המדובר, מכיוון שלכל אחד מהם סמנטיקה שונה.

וקטורים הם האוסף היחיד שמאפשר לנו לקבל ערך שרירותי כלשהו לפי אינדקס. זה נעשה על ידי הערכת הווקטור והאינדקס כביטוי:

user => (my-vector 2); [1 2 3] 3

אנחנו יכולים לעשות את אותו הדבר, באמצעות אותו תחביר, גם עבור מפות:

משתמש => (המפה שלי: ב) 2

יש לנו גם פונקציות לגישה לווקטורים ולרשימות כדי לקבל את הערך הראשון, את הערך האחרון ואת שאר הרשימה:

משתמש => (הראשון הווקטור שלי) משתמש אחד => (הרשימה האחרונה שלי) 3 המשתמש => (הבא הווקטור שלי) (2 3)

למפות יש פונקציות נוספות כדי לקבל את כל רשימת המקשים והערכים:

משתמש => (מקשים my-map) (: a: b) user => (vals my-map) (1 2)

הגישה האמיתית היחידה שיש לנו להגדיר היא לראות אם אלמנט מסוים הוא חבר.

זה נראה דומה מאוד לגישה לכל אוסף אחר:

user => (my-set 1) 1 user => (my-set 5) null

5.3. זיהוי אוספים

ראינו שאופן הגישה לאוסף משתנה בהתאם לסוג האוסף שיש לנו. יש לנו קבוצה של פונקציות שבהן אנו יכולים להשתמש כדי לקבוע זאת, הן באופן ספציפי והן כללי יותר.

לכל אחד מהאוספים שלנו פונקציה ספציפית לקבוע אם ערך נתון הוא מסוג זה - רשימה? לרשימות, מַעֲרֶכֶת? לסטים וכן הלאה. בנוסף, יש seq? לקביעה אם ערך נתון הוא a משך מכל סוג שהוא, ו אסוציאטיבי? כדי לקבוע אם ערך נתון מאפשר גישה אסוציאטיבית מכל סוג שהוא - כלומר וקטורים ומפות:

משתמש => (וקטור? [1 2 3]); וקטור הוא משתמש אמיתי וקטורי => (וקטור? # {1 2 3}); קבוצה אינה משתמש שקר וקטורי => (רשימה? '(1 2 3)); רשימה היא רשימה אמיתית משתמש => (רשימה? [1 2 3]); וקטור אינו משתמש שקר ברשימה => (מפה? {: A 1: ב 2}); מפה היא משתמש אמיתי במפה => (מפה? # {1 2 3}); קבוצה אינה משתמש כוזב במפה => (seq? '(1 2 3)); רשימה היא משתמש אמיתי seq => (seq? [1 2 3]); וקטור אינו משתמש שקר seq => (seq? (Seq [1 2 3])); ניתן להמיר וקטור למשתמש אמיתי seq => (אסוציאטיבי? {: A 1: ב 2}); מפה היא משתמש אמיתי אסוציאטיבי => (אסוציאטיבי? [1 2 3]); וקטור הוא משתמש אמיתי אסוציאטיבי => (אסוציאטיבי? '(1 2 3)); רשימה אינה שקרית אסוציאטיבית

5.4. אוספים מוטציה

בקלוז'ור, כמו ברוב השפות הפונקציונאליות, כל האוספים אינם ניתנים לשינוי. כל דבר שאנו עושים כדי לשנות אוסף מביא ליצירת אוסף חדש לגמרי שייצג את השינויים. זה יכול לתת יתרונות אדירים של יעילות ומשמעותו שאין סיכון לתופעות לוואי בשוגג.

עם זאת, עלינו להקפיד שנבין זאת, אחרת השינויים הצפויים באוספים שלנו לא יתרחשו.

הוספת אלמנטים חדשים לווקטור, רשימה או סט נעשית באמצעות conj. זה עובד בצורה שונה בכל אחד מהמקרים הללו, אך באותה כוונה בסיסית:

משתמש => (conj [1 2 3] 4); מוסיף לסוף [1 2 3 4] משתמש => (conj '(1 2 3) 4); מוסיף למתחיל (4 1 2 3) משתמש => (conj # {1 2 3} 4); משתמש לא מסודר {1 4 3 2} => (conj # {1 2 3} 3); הוספת ערך שנמצא כבר לא עושה דבר # {1 3 2}

אנו יכולים גם להסיר ערכים מערכה באמצעות disj. שים לב שזה לא עובד ברשימה או בווקטור מכיוון שהם מסודרים בקפדנות:

user => (disj # {1 2 3} 2); מסיר את הערך מספר {1 3} user => (disj # {1 2 3} 4); לא עושה דבר מכיוון שהערך לא היה קיים מס '{1 3 2}

הוספת אלמנטים חדשים למפה נעשית באמצעות assoc. אנו יכולים גם להסיר ערכים ממפה באמצעות דיסוק:

user => (assoc {: a 1: b 2}: c 3); מוסיף מפתח חדש {: a 1,: b 2,: c 3} user => (assoc {: a 1: b 2}: b 3); מעדכן מפתח קיים {: a 1,: b 3} user => (dissoc {: a 1: b 2}: b); מסיר מפתח קיים {: a 1} user => (dissoc {: a 1: b 2}: c); לא עושה דבר מכיוון שהמפתח לא היה קיים {: a 1,: b 2}

5.5. קונסטרוקציות תכנות פונקציונליות

Clojure היא, בבסיסה, שפת תכנות פונקציונלית. זה אומר ש יש לנו גישה להרבה מושגי תכנות פונקציונליים מסורתיים - כגון מפה, מסנן, ו לְהַפחִית. אלה פועלים בדרך כלל כמו בשפות אחרות. התחביר המדויק עשוי להיות שונה במקצת.

באופן ספציפי, פונקציות אלה בדרך כלל לוקחות את הפונקציה להחלתה כארגומנט הראשון, ואת האוסף להחלתה כארגומנט השני:

משתמש => (מפה כולל [1 2 3]); הגדל כל ערך במשתמש הווקטורי (2 3 4) => (מפה כולל מספר {1 2 3}); הגדל כל ערך בערכה (2 4 3) משתמש => (מסנן מוזר? [1 2 3 4 5]); החזר רק ערכים מוזרים (1 3 5) משתמש => (הסר מוזר? [1 2 3 4 5]); החזר רק ערכים שאינם מוזרים (2 4) משתמש => (צמצם + [1 2 3 4 5]); הוסף את כל הערכים יחד, והחזיר את הסכום 15

6. מבני בקרה

כמו בכל שפות המטרה הכללית, Clojure כולל קריאות למבני בקרה סטנדרטיים, כגון תנאים ולולאות.

6.1. תנאים

התנאים מטופלים על ידי אם הַצהָרָה. זה לוקח שלושה פרמטרים: בדיקה, בלוק לבצע אם הבדיקה היא נָכוֹן, וחסימה לביצוע אם הבדיקה היא שֶׁקֶר. כל אחד מאלה יכול להיות ערך פשוט או רשימה רגילה שתוערך על פי דרישה:

משתמש => (אם נכון 1 2) משתמש אחד => (אם שקר 1 2) 2

המבחן שלנו יכול להיות כל דבר שאנחנו צריכים - זה לא צריך להיות a אמת שקר ערך. זה יכול להיות גם בלוק שמוערך כדי לתת לנו את הערך שאנחנו צריכים:

user => (if (> 1 2) "נכון" "שקר") "שקר"

כל הצ'קים הסטנדרטיים, כולל =, >, ו <, ניתן להשתמש כאן. יש גם קבוצה של פרדיקטים שניתן להשתמש בהם מסיבות שונות אחרות - ראינו כמה כבר כשבוחנים אוספים, למשל:

user => (אם (אי זוגי? 1) "1 מוזר" "1 הוא זוגי") "1 הוא אי זוגי"

המבחן יכול להחזיר כל ערך בכלל - הוא לא צריך להיות רק נָכוֹן אוֹ שֶׁקֶר. עם זאת, זה נחשב שכן נָכוֹן אם הערך הוא משהו למעט שֶׁקֶר אוֹ אֶפֶס. זה שונה מהדרך שבה JavaScript פועל, כאשר יש מערך גדול של ערכים שנחשבים "אמת-y" אך לא נָכוֹן:

user => (if 0 "True" "False") "True" user => (if [] "True" "False") "True" user => (if null "True" "False") "שקר"

6.2. לולאה

התמיכה הפונקציונלית שלנו באוספים מטפלת בחלק ניכר מעבודות הלופינג - במקום לכתוב לולאה על האוסף, אנו משתמשים בפונקציות הסטנדרטיות ונותנים לשפה לעשות את האיטרציה עבורנו.

מחוץ לכך, לולאה נעשית לחלוטין באמצעות רקורסיה. אנו יכולים לכתוב פונקציות רקורסיביות או להשתמש ב לוּלָאָה ו לְהִתְרַחֵשׁ שֵׁנִית מילות מפתח לכתיבת לולאה בסגנון רקורסיבי:

user => (loop [accum [] i 0] (if (= i 10) accum (recur (conj accum i) (inc i)))) [0 1 2 3 4 5 6 7 8 9]

ה לוּלָאָה שיחה מתחילה חסימה פנימית שמבוצעת בכל איטרציה ומתחילה בהגדרת פרמטרים ראשוניים. ה לְהִתְרַחֵשׁ שֵׁנִית ואז שיחות חוזרות לולאה ומספקות את הפרמטרים הבאים לשימוש עבור האיטרציה. אם לְהִתְרַחֵשׁ שֵׁנִית לא נקרא, ואז הלולאה מסתיימת.

במקרה זה, אנו לולאות בכל פעם שה- אני הערך אינו שווה ל- 10, ואז ברגע שהוא שווה ל- 10, במקום זאת אנו מחזירים את הווקטור המצטבר של המספרים.

7. סיכום

מאמר זה נתן הקדמה לשפת התכנות Clojure ומראה כיצד התחביר עובד וכמה מהדברים שתוכלו לעשות בו. זו רק דרגת היכרות ולא נכנסת לעומק כל מה שאפשר לעשות עם השפה.

עם זאת, מדוע לא להרים אותו, לנסות ולראות מה אתה יכול לעשות איתו.


$config[zx-auto] not found$config[zx-overlay] not found