Inleiding tot Clojure

1. Inleiding

Clojure is een functionele programmeertaal die volledig draait op de Java Virtual Machine, vergelijkbaar met Scala en Kotlin. Clojure wordt beschouwd als een Lisp-derivaat en zal bekend zijn bij iedereen die ervaring heeft met andere Lisp-talen.

Deze tutorial geeft een inleiding tot de Clojure-taal, laat zien hoe je ermee aan de slag kunt en enkele van de belangrijkste concepten van hoe het werkt.

2. Clojure installeren

Clojure is beschikbaar als installatieprogramma en gemaksscripts voor gebruik op Linux en macOS. Helaas heeft Windows in dit stadium niet zo'n installatieprogramma.

De Linux-scripts kunnen echter werken in zoiets als Cygwin of Windows Bash. Er is ook een online service die kan worden gebruikt om de taal uit te testen, en oudere versies hebben een zelfstandige versie die kan worden gebruikt.

2.1. Standalone downloaden

Het zelfstandige JAR-bestand kan worden gedownload vanaf Maven Central. Helaas werken versies nieuwer dan 1.8.0 niet meer zo gemakkelijk omdat het JAR-bestand is opgesplitst in kleinere modules.

Zodra dit JAR-bestand is gedownload, kunnen we het gebruiken als een interactieve REPL door het simpelweg te behandelen als een uitvoerbare JAR:

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

2.2. Webinterface om te REPL

Een webinterface naar de Clojure REPL is beschikbaar op //repl.it/languages/clojure die we kunnen proberen zonder iets te hoeven downloaden. Momenteel ondersteunt dit alleen Clojure 1.8.0 en niet de nieuwere releases.

2.3. Installatieprogramma op MacOS

Als je macOS gebruikt en Homebrew hebt geïnstalleerd, dan kan de nieuwste release van Clojure eenvoudig worden geïnstalleerd:

$ brew installeer clojure

Dit ondersteunt de nieuwste versie van Clojure - 1.10.0 op het moment van schrijven. Eenmaal geïnstalleerd, kunnen we de REPL laden door simpelweg de clojure of clj commando's:

$ clj Clojure 1.10.0 gebruiker =>

2.4. Installatieprogramma op Linux

Een zelfinstallerend shellscript is beschikbaar om de tools op Linux te installeren:

$ 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

Net als bij het macOS-installatieprogramma, zijn deze beschikbaar voor de meest recente releases van Clojure en kunnen ze worden uitgevoerd met de clojure of clj commando's.

3. Inleiding tot de Clojure REPL

Alle bovenstaande opties geven ons toegang tot de Clojure REPL. Dit is het directe Clojure-equivalent van de JShell-tool voor Java 9 en hoger en stelt ons in staat om Clojure-code in te voeren en het resultaat direct direct te zien. Dit is een fantastische manier om te experimenteren en te ontdekken hoe bepaalde taalfuncties werken.

Zodra de REPL is geladen, hebben we een prompt waarop elke standaard Clojure-code kan worden ingevoerd en onmiddellijk kan worden uitgevoerd. Dit omvat eenvoudige Clojure-constructies, evenals interactie met andere Java-bibliotheken - hoewel ze beschikbaar moeten zijn op het klassenpad om te worden geladen.

De prompt van de REPL is een indicatie van de huidige naamruimte waarin we werken. Voor het merendeel van ons werk is dit de gebruiker naamruimte, en dus zal de prompt zijn:

gebruiker =>

Alles in de rest van dit artikel gaat ervan uit dat we toegang hebben tot de Clojure REPL, en werken allemaal rechtstreeks in een dergelijke tool.

4. Taalbeginselen

De Clojure-taal ziet er heel anders uit dan veel andere op JVM gebaseerde talen, en zal in het begin mogelijk erg ongebruikelijk lijken. Het wordt beschouwd als een dialect van Lisp en heeft een vergelijkbare syntaxis en functionaliteit als andere Lisp-talen.

Veel van de code die we in Clojure schrijven - net als bij andere Lisp-dialecten - wordt uitgedrukt in de vorm van lijsten. Lijsten kunnen vervolgens worden geëvalueerd om resultaten te produceren - in de vorm van meer lijsten of eenvoudige waarden.

Bijvoorbeeld:

(+ 1 2) ; = 3

Dit is een lijst die uit drie elementen bestaat. Het "+" - symbool geeft aan dat we deze oproep - optellen uitvoeren. De overige elementen worden vervolgens bij deze aanroep gebruikt. Dit resulteert dus in "1 + 2".

Door hier een List-syntaxis te gebruiken, kan deze triviaal worden uitgebreid. We kunnen bijvoorbeeld doen:

(+ 1 2 3 4 5) ; = 15

En dit resulteert in "1 + 2 + 3 + 4 + 5".

Let ook op de puntkomma. Dit wordt in Clojure gebruikt om een ​​opmerking aan te duiden en is niet het einde van de uitdrukking zoals we zouden zien in Java.

4.1. Gemakkelijk Types

Clojure is bovenop de JVM gebouwd en als zodanig hebben we toegang tot dezelfde standaardtypen als elke andere Java-applicatie. Typen worden doorgaans automatisch afgeleid en hoeven niet expliciet te worden gespecificeerd.

Bijvoorbeeld:

123; Lang 1,23; Dubbel "Hallo"; Tekenreeks waar; Boolean

We kunnen ook wat meer gecompliceerde typen specificeren, met behulp van speciale voorvoegsels of achtervoegsels:

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

Merk op dat de clojure.lang.BigInt type wordt gebruikt in plaats van java.math.BigInteger. Dit komt omdat het Clojure-type enkele kleine optimalisaties en fixes heeft.

4.2. Sleutelwoorden en symbolen

Clojure geeft ons het concept van zowel trefwoorden als symbolen. Sleutelwoorden verwijzen alleen naar zichzelf en worden vaak gebruikt voor zaken als kaartsleutels. Symbolen daarentegen zijn namen die worden gebruikt om naar andere dingen te verwijzen. Variabeldefinities en functienamen zijn bijvoorbeeld symbolen.

We kunnen trefwoorden construeren door een naam te gebruiken die wordt voorafgegaan door een dubbele punt:

gebruiker =>: kw: kw gebruiker =>: a: a

Sleutelwoorden hebben directe gelijkheid met zichzelf, en niet met iets anders:

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

De meeste andere dingen in Clojure die geen eenvoudige waarden zijn, worden als symbolen beschouwd. Deze evalueren naar alles waarnaar ze verwijzen, terwijl een zoekwoord altijd naar zichzelf evalueert:

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

4.3. Naamruimten

De Clojure-taal heeft het concept van naamruimten voor het organiseren van onze code. Elk stukje code dat we schrijven, leeft in een naamruimte.

Standaard wordt de REPL uitgevoerd in het gebruiker namespace - zoals gezien door de prompt met de vermelding "user =>".

We kunnen naamruimten maken en wijzigen met de NS trefwoord:

gebruiker => (ns new.ns) nihil new.ns =>

Zodra we de naamruimten hebben gewijzigd, is alles wat in de oude is gedefinieerd niet langer beschikbaar voor ons, en alles wat in de nieuwe is gedefinieerd, is nu beschikbaar.

We hebben toegang tot definities in naamruimten door ze volledig te kwalificeren. Bijvoorbeeld de naamruimte clojure.string definieert een functie hoofdletters.

Als we in de clojure.string namespace, we hebben er direct toegang toe. Als we dat niet zijn, moeten we het kwalificeren als clojure.string / hoofdletters:

user => (clojure.string / hoofdletters "hallo") "HELLO" user => (hoofdletters "hallo"); Dit is niet zichtbaar in de "gebruiker" naamruimte Syntaxisfout bij compileren op (REPL: 1: 1). Kan symbool niet oplossen: hoofdletters in deze context user => (ns clojure.string) nul clojure.string => (hoofdletters "hallo"); Dit is zichtbaar omdat we ons nu in de "clojure.string" naamruimte "HELLO" bevinden

We kunnen ook de vereisentrefwoord om op een gemakkelijkere manier toegang te krijgen tot definities vanuit een andere naamruimte. Er zijn twee belangrijke manieren waarop we dit kunnen gebruiken - om een ​​naamruimte te definiëren met een kortere naam zodat deze gemakkelijker te gebruiken is, en om rechtstreeks toegang te krijgen tot definities vanuit een andere naamruimte zonder een voorvoegsel:

clojure.string => (vereisen '[clojure.string: as str]) nul clojure.string => (str / hoofdletter "Hallo") "HELLO" user => (vereisen' [clojure.string: als str: verwijs [hoofdletters]]) nihil gebruiker => (hoofdletter "Hallo") "HALLO"

Beide hebben alleen invloed op de huidige naamruimte, dus als u naar een andere wilt overschakelen, moet u een nieuwe hebben vereist. Dit helpt om onze naamruimten schoner te houden en geeft ons alleen toegang tot wat we nodig hebben.

4.4. Variabelen

Als we eenmaal weten hoe we eenvoudige waarden moeten definiëren, kunnen we ze aan variabelen toewijzen. We kunnen dit doen met behulp van het trefwoord def:

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

Zodra we dit hebben gedaan, kunnen we het symbool gebruiken eenoveral waar we deze waarde willen vertegenwoordigen:

user => een 123

Variabele definities kunnen zo eenvoudig of gecompliceerd zijn als we willen.

Om bijvoorbeeld een variabele te definiëren als de som van getallen, kunnen we het volgende doen:

gebruiker => (def b (+ 1 2 3 4 5)) # 'gebruiker / b gebruiker => b 15

Merk op dat we de variabele nooit hoeven te declareren of aan te geven welk type het is. Clojure bepaalt dit alles automatisch voor ons.

Als we een variabele proberen te gebruiken die niet is gedefinieerd, krijgen we in plaats daarvan een foutmelding:

user => onbekende syntaxisfout bij compileren op (REPL: 0: 0). Kan symbool niet oplossen: onbekend in deze context gebruiker => (def c (+ 1 onbekend)) Syntaxisfout bij compileren op (REPL: 1: 8). Kan symbool niet oplossen: onbekend in deze context

Merk op dat de uitvoer van de def functie ziet er iets anders uit dan de invoer. Een variabele definiëren een geeft een reeks terug van ‘Gebruiker / a. Dit komt doordat het resultaat een symbool is, en dit symbool is gedefinieerd in de huidige naamruimte.

4.5. Functies

We hebben al een paar voorbeelden gezien van het aanroepen van functies in Clojure. We maken een lijst die begint met de aan te roepen functie en vervolgens alle parameters.

Wanneer deze lijst evalueert, krijgen we de retourwaarde van de functie. Bijvoorbeeld:

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

We kunnen ook aanroepen naar functies nesten, want als we de uitvoer van een functieaanroep als parameter aan een andere willen doorgeven:

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

Ook, we kunnen ook onze functies definiëren als we dat willen. Functies worden gemaakt met behulp van de fn opdracht:

user => (fn [a b] (println "Nummers toevoegen" a "en" b) (+ a b)) #object [user $ eval165 $ fn__166 0x5644dc81 "[email protected]"]

Helaas, dit geeft de functie geen naam die kan worden gebruikt. In plaats daarvan kunnen we een symbool definiëren dat deze functie vertegenwoordigt met zeker, precies zoals we hebben gezien voor variabelen:

user => (def add (fn [a b] (println "Getallen toevoegen" a "en" b) (+ a b))) # 'user / add

Nu we deze functie hebben gedefinieerd, kunnen we deze hetzelfde noemen als elke andere functie:

gebruiker => (voeg 1 2 toe) Toevoegen van nummers 1 en 2 3

Voor het gemak Clojure stelt ons ook in staat om te gebruiken defn om in één keer een functie met een naam te definiëren.

Bijvoorbeeld:

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

4.6. Let en lokale variabelen

De def call definieert een symbool dat globaal is voor de huidige naamruimte. Dit is doorgaans niet wat gewenst is bij het uitvoeren van code. In plaats daarvan, Clojure biedt de laat oproep om variabelen lokaal voor een blok te definiëren. Dit is vooral handig wanneer u ze binnen functies gebruikt, waarbij u niet wilt dat de variabelen buiten de functie lekken.

We zouden bijvoorbeeld onze subfunctie kunnen definiëren:

user => (defn sub [a b] (def resultaat (- a b)) (println "Result:" resultaat) resultaat) # 'gebruiker / sub

Het gebruik hiervan heeft echter de volgende onverwachte bijwerking:

gebruiker => (sub 1 2) Resultaat: -1 -1 gebruiker => resultaat; Nog steeds zichtbaar buiten de functie -1

Laten we het in plaats daarvan herschrijven met laat:

user => (defn sub [ab] (let [result (- ab)] (println "Result:" result) result)) # 'user / sub user => (sub 1 2) Resultaat: -1 -1 user = > resultaat Syntaxisfout compileren op (REPL: 0: 0). Kan symbool niet oplossen: resultaat in deze context

Dit keer de resultaat symbool is niet zichtbaar buiten de functie. Of, inderdaad, buiten de laat blok waarin het werd gebruikt.

5. Collecties

Tot nu toe hebben we vooral interactie gehad met eenvoudige waarden. We hebben ook lijsten gezien, maar meer niet. Clojure heeft wel een volledige set verzamelingen die kunnen worden gebruikt, bestaande uit lijsten, vectoren, kaarten en sets:

  • Een vector is een geordende lijst met waarden - elke willekeurige waarde kan in een vector worden geplaatst, inclusief andere verzamelingen.
  • Een set is een ongeordende verzameling waarden en kan dezelfde waarde nooit meer dan één keer bevatten.
  • Een kaart is een eenvoudige set sleutel / waarde-paren. Het is heel gebruikelijk om trefwoorden te gebruiken als de sleutels in een kaart, maar we kunnen elke gewenste waarde gebruiken, inclusief andere verzamelingen.
  • Een lijst lijkt erg op een vector. Het verschil is vergelijkbaar met dat tussen een ArrayList en een LinkedList in Java. Meestal heeft een vector de voorkeur, maar een lijst is beter als we elementen aan het begin willen toevoegen, of als we de elementen alleen in opeenvolgende volgorde willen benaderen.

5.1. Collecties bouwen

Elk van deze kan worden gemaakt met behulp van een verkorte notatie of met behulp van een functieaanroep:

; Vector gebruiker => [1 2 3] [1 2 3] gebruiker => (vector 1 2 3) [1 2 3]; Lijst gebruiker => '(1 2 3) (1 2 3) gebruiker => (lijst 1 2 3) (1 2 3); Set user => # {1 2 3} # {1 3 2} user => (hash-set 1 2 3) # {1 3 2}; Map user => {: a 1: b 2} {: a 1,: b 2} user => (hash-map: a 1: b 2) {: b 2,: a 1}

Merk op dat de Set en Kaart voorbeelden retourneren de waarden niet in dezelfde volgorde. Dit komt doordat deze verzamelingen inherent ongeordend zijn, en wat we zien hangt af van hoe ze in het geheugen worden weergegeven.

We kunnen ook zien dat de syntaxis voor het maken van een lijst erg lijkt op de standaard Clojure-syntaxis voor expressies. Een Clojure-uitdrukking is in feite een lijst die wordt geëvalueerd, terwijl het apostrof-teken hier aangeeft dat we de werkelijke lijst met waarden willen in plaats van deze te evalueren.

We kunnen natuurlijk een verzameling aan een variabele toewijzen op dezelfde manier als elke andere waarde. We kunnen de ene collectie ook gebruiken als sleutel of waarde binnen een andere collectie.

Lijsten worden beschouwd als een seq. Dit betekent dat de klasse het ISeq koppel. Alle andere collecties kunnen worden geconverteerd naar een seq de ... gebruiken seq functie:

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. Toegang tot collecties

Zodra we een verzameling hebben, kunnen we ermee communiceren om waarden weer naar buiten te krijgen. Hoe we dit kunnen doen, hangt enigszins af van de verzameling in kwestie, aangezien elk van hen een andere semantiek heeft.

Vectoren zijn de enige verzameling waarmee we elke willekeurige waarde per index kunnen krijgen. Dit wordt gedaan door de vector en index als een uitdrukking te evalueren:

gebruiker => (mijn-vector 2); [1 2 3] 3

We kunnen hetzelfde doen, met dezelfde syntaxis, ook voor kaarten:

gebruiker => (mijn-kaart: b) 2

We hebben ook functies voor toegang tot vectoren en lijsten om de eerste waarde, de laatste waarde en de rest van de lijst te krijgen:

user => (eerste mijn-vector) 1 gebruiker => (laatste mijn-lijst) 3 gebruiker => (volgende mijn-vector) (2 3)

Kaarten hebben extra functies om de volledige lijst met sleutels en waarden te krijgen:

user => (keys my-map) (: a: b) user => (vals my-map) (1 2)

De enige echte toegang die we hebben om in te stellen, is om te zien of een bepaald element een lid is.

Dit lijkt erg op toegang tot een andere verzameling:

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

5.3. Collecties identificeren

We hebben gezien dat de manier waarop we toegang krijgen tot een collectie varieert afhankelijk van het type collectie dat we hebben. We hebben een reeks functies die we kunnen gebruiken om dit te bepalen, zowel op een specifieke als meer generieke manier.

Elk van onze collecties heeft een specifieke functie om te bepalen of een bepaalde waarde van dat type is - lijst? voor lijsten, ingesteld? voor sets, enzovoort. Bovendien is er seq? om te bepalen of een bepaalde waarde een is seq van welke aard dan ook, en associatief? om te bepalen of een bepaalde waarde associatieve toegang van welke aard dan ook toestaat - dat wil zeggen vectoren en kaarten:

gebruiker => (vector? [1 2 3]); Een vector is een vector true user => (vector? # {1 2 3}); Een set is geen vector false user => (lijst? '(1 2 3)); Een lijst is een lijst echte gebruiker => (lijst? [1 2 3]); Een vector is geen lijst false user => (map? {: A 1: b 2}); Een kaart is een echte gebruiker => (kaart? # {1 2 3}); Een set is geen map false user => (seq? '(1 2 3)); Een lijst is een seq echte gebruiker => (seq? [1 2 3]); Een vector is geen seq false user => (seq? (Seq [1 2 3])); Een vector kan worden omgezet in een seq true user => (associatief? {: A 1: b 2}); Een kaart is associatief true user => (associatief? [1 2 3]); Een vector is associatief true user => (associatief? '(1 2 3)); Een lijst is niet associatief onwaar

5.4. Collecties muteren

In Clojure zijn, zoals bij de meeste functionele talen, alle verzamelingen onveranderlijk. Alles wat we doen om een ​​collectie te veranderen, resulteert in een gloednieuwe collectie die de wijzigingen vertegenwoordigt. Dit kan enorme efficiëntievoordelen opleveren en betekent dat er geen risico is op onbedoelde bijwerkingen.

We moeten echter ook oppassen dat we dit begrijpen, anders zullen de verwachte wijzigingen in onze collecties niet plaatsvinden.

Het toevoegen van nieuwe elementen aan een vector, lijst of set doet u met conj. Dit werkt in elk van deze gevallen anders, maar met dezelfde basisbedoeling:

gebruiker => (conj [1 2 3] 4); Telt op aan het einde [1 2 3 4] user => (conj '(1 2 3) 4); Voegt toe aan het begin (4 1 2 3) gebruiker => (conj # {1 2 3} 4); Ongeordende # {1 4 3 2} gebruiker => (conj # {1 2 3} 3); Het toevoegen van een reeds aanwezig item doet niets # {1 3 2}

We kunnen ook items uit een set verwijderen met disj. Merk op dat dit niet werkt op een lijst of vector, omdat ze strikt geordend zijn:

gebruiker => (disj # {1 2 3} 2); Verwijdert het item # {1 3} user => (disj # {1 2 3} 4); Doet niets omdat het item niet aanwezig was # {1 3 2}

Nieuwe elementen aan een kaart toevoegen doe je met assoc. We kunnen ook items van een kaart verwijderen met dissocie:

user => (assoc {: a 1: b 2}: c 3); Voegt een nieuwe sleutel toe {: a 1,: b 2,: c 3} user => (assoc {: a 1: b 2}: b 3); Werkt een bestaande sleutel bij {: a 1,: b 3} user => (dissoc {: a 1: b 2}: b); Verwijdert een bestaande sleutel {: a 1} user => (dissoc {: a 1: b 2}: c); Doet niets omdat de sleutel niet aanwezig was {: a 1,: b 2}

5.5. Functionele programmeerconstructies

Clojure is in wezen een functionele programmeertaal. Dit betekent dat we hebben toegang tot veel traditionele functionele programmeerconcepten - zoals kaart, filter, en verminderen. Deze werken over het algemeen hetzelfde als in andere talen. De exacte syntaxis kan echter enigszins afwijken.

In het bijzonder nemen deze functies over het algemeen de functie die moet worden toegepast als het eerste argument, en de verzameling waarop deze moet worden toegepast als het tweede argument:

gebruiker => (kaart inc [1 2 3]); Verhoog elke waarde in de vector (2 3 4) user => (map inc # {1 2 3}); Verhoog elke waarde in de set (2 4 3) user => (filter oneven? [1 2 3 4 5]); Retourneer alleen oneven waarden (1 3 5) user => (verwijder oneven? [1 2 3 4 5]); Retourneer alleen niet-oneven waarden (2 4) user => (reduce + [1 2 3 4 5]); Tel alle waarden bij elkaar op en retourneer de som 15

6. Controlestructuren

Zoals bij alle talen voor algemeen gebruik, vraagt ​​Clojure om standaard controlestructuren, zoals conditionals en loops.

6.1. Voorwaardelijke

Conditionals worden afgehandeld door de als uitspraak. Hiervoor zijn drie parameters nodig: een test, een blok om uit te voeren als de test is waar, en een blok om uit te voeren als de test is false. Elk van deze kan een eenvoudige waarde zijn of een standaardlijst die op aanvraag wordt geëvalueerd:

gebruiker => (indien waar 1 2) 1 gebruiker => (indien niet waar 1 2) 2

Onze test kan alles zijn wat we nodig hebben - het hoeft geen waar onwaar waarde. Het kan ook een blok zijn dat wordt geëvalueerd om ons de waarde te geven die we nodig hebben:

user => (if (> 1 2) "True" "False") "False"

Alle standaardcontroles, inclusief =, >, en <, kan hier worden gebruikt. Er is ook een reeks predikaten die om verschillende andere redenen kunnen worden gebruikt - we hebben er al een aantal gezien bij het bekijken van collecties, bijvoorbeeld:

gebruiker => (if (oneven? 1) "1 is oneven" "1 is even") "1 is oneven"

De test kan elke waarde retourneren - dat hoeft niet alleen zo te zijn waar of false. Het wordt echter geacht te zijn waar als de waarde iets anders is dan false of nihil. Dit verschilt van de manier waarop JavaScript werkt, waar er een grote reeks waarden is die als 'waarheid-y' worden beschouwd, maar niet waar:

user => (if 0 "True" "False") "True" user => (if [] "True" "False") "True" user => (indien nihil "True" "False") "False"

6.2. Looping

Onze functionele ondersteuning voor collecties zorgt voor een groot deel van het looping-werk - in plaats van een loop over de collectie te schrijven, gebruiken we de standaardfuncties en laten we de taal de iteratie voor ons doen.

Buiten dit gebeurt het herhalen volledig door middel van recursie. We kunnen recursieve functies schrijven, of we kunnen de lus en terugkomen trefwoorden om een ​​recursieve stijllus te schrijven:

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]

De lus call start een binnenblok dat wordt uitgevoerd bij elke iteratie en begint met het instellen van enkele initiële parameters. De terugkomen call roept vervolgens terug in de lus, met de volgende parameters die voor de iteratie moeten worden gebruikt. Als terugkomen wordt niet aangeroepen, dan eindigt de lus.

In dit geval herhalen we elke keer dat het ik waarde is niet gelijk aan 10, en zodra deze gelijk is aan 10, retourneren we in plaats daarvan de geaccumuleerde vector van getallen.

7. Samenvatting

Dit artikel geeft een inleiding tot de programmeertaal Clojure en laat zien hoe de syntaxis werkt en enkele dingen die je ermee kunt doen. Dit is slechts een inleidend niveau en gaat niet in op de diepte van alles wat er met de taal kan worden gedaan.

Maar waarom pak je het niet op, probeer het eens en kijk wat je ermee kunt doen.


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