பதிப்புரிமை

இந்தப் புத்தகம் GFDL https://www.gnu.org/licenses/fdl-1.3.html உரிமத்தின் கீழ் வெளியிடப்பட்டுள்ளது.

ஆசிரியர்

இந்தப் புத்தகம் Karthikeyan.A.K-ஆல் எழுதப்பட்டது. என்னை mindaslab@protonmail.com என்ற மின்னஞ்சல் முகவரியிலோ அல்லது WhatsApp, Signal அல்லது Telegram மூலம் +91 8428050777 என்ற எண்ணிலோ தொடர்பு கொள்ளலாம்.

முன்நிபந்தனைகள்

இருப்பினும், ஒரு விஷயம், உங்களுக்கு கணினிகளைப் பயன்படுத்துவதில் பரிச்சயம் இருக்க வேண்டும். நீங்கள் Ubuntu GNU/Linux-ஐப் பற்றி நன்கு தெரிந்து வைத்திருக்க வேண்டும் என்று நான் பரிந்துரைக்கிறேன், அதை நீங்கள் இங்கே பதிவிறக்கம் செய்யலாம் https://ubuntu.com. அதை ஒரு கணினியில் நிறுவி, அதைப் பயன்படுத்தக் கற்றுக்கொண்டு, GNU/Linux-உடன் பழகிக் கொள்ளுங்கள். GNU/Linux-ஐப் பற்றி மேலும் தெரிந்துகொள்ள http://linuxjourney.com என்ற இணையதளத்தைப் பார்வையிடலாம்.

இந்த புத்தகம் ஏன்?

தெரியாத காரணத்தால் மிகவும் புத்திசாலித்தனமான நிரலாளர்கள் Clojure-ஐப் பயன்படுத்துகிறார்கள், அது ஏன் என்று எனக்கு இன்னும் புரியவில்லை. அதற்கான காரணத்தைக் கண்டறியவே இந்தப் புத்தகம் எழுதப்படுகிறது. எனது முந்தைய வேலை ஒரு மருத்துவக் குறியீட்டு (medical coding) நிறுவனத்தில் இருந்தது, அங்கு நாங்கள் 3 Rails செயலிகளைப் பராமரித்தோம். அவற்றில் இரண்டில் எனக்குக் குறியீடுகளை எழுத வாய்ப்பு கிடைத்தது. ஒன்று 0.1 மில்லியனுக்கும் அதிகமான வரிகளைக் கொண்டிருந்தது, அதை ஒரு திறமையான நிரலாளர் வழிநடத்தினார் மற்றும் அது சரியாகப் பராமரிக்கப்பட்டது. மற்றொன்று 0.2 மில்லியனுக்கும் அதிகமான வரிகளைக் கொண்டிருந்தது, அதில் எந்த சோதனையும் (test) இல்லை, அதை வழிநடத்திய ஒருவருக்குக் குறியீடுகளை எழுதத் தெரியாது ஆனால் அரசியல் செய்வதில் வல்லவர். அந்த பிராஜெக்ட் பராமரிக்கக்கூடிய மென்பொருளை எவ்வாறு எழுதுவது என்று தெரியாதவர்களால் வழிநடத்தப்பட்டது, அதனால் அந்த பிராஜெக்ட்டின் மூத்த மேம்பாட்டாளர்கள் கடந்த காலத் தவறுகளின் பாதிப்புகளை அனுபவிக்க வேண்டியிருந்தது. பராமரிக்கப்படாமல் விட்டால் Rails செயலிகள் எவ்வளவு மோசமாகலாம் என்பதை அது எனக்குக் காட்டியது, எனவே இன்னும் எளிமையான, தவறுகள் நடக்காத (idiot-proof) ஒன்றைத் தேட விரும்புகிறேன்.

நான் முதன்முதலில் Ruby on Rails-ஐச் சந்தித்தபோது, இணையப் பக்கங்களை உருவாக்குவதில் PHP-தான் ராஜாவாக இருக்கும் என்று நான் உறுதியாக இருந்தேன், அது இன்றும் உண்மைதான். ஆனால் Rails எனது வாழ்க்கையை மாற்றியது மற்றும் எனது உற்பத்தித் திறனை அதிகரித்தது. Rails மூலம் மிகப்பெரிய திட்டங்களைக் கையாள முடியாது என்று நான் சொல்ல மாட்டேன்; எனது தற்போதைய வேலையில் மிகவும் திறமையானவர்களுடன் இணைந்து நானும் அதைத்தான் செய்து வருகிறேன். ஆனால் இந்தப் புத்தகம் ஒரு தேடல் மட்டுமே. எனது PHP நாட்களில் Ruby-யின் சக்தியை உணர்ந்தபோது நான் செய்த அதே போன்ற ஒரு தேடலை இப்போது செய்கிறேன். Clojure போதுமான அளவு சிறந்ததா இல்லையா என்பதை அறிய எனது தேடலை ஆவணப்படுத்தவே இந்தப் புத்தகம்.

நான் Clojure-க்குள் நுழைய மற்றொரு காரணம் ClojureScript. கிையண்ட் (client) மற்றும் சர்வர் (server) ஆகிய இரு பக்கங்களிலும் ஒரே மொழியைப் பயன்படுத்த முடிந்தால், அது பயமுறுத்தும் JavaScript இல்லை என்றால், அத்தகைய மொழியை யார் ஆராய விரும்ப மாட்டார்கள்? Lisp ஒரு பழைய மொழியாகத் தெரிகிறது, Clojure இன்றைய காலத்திற்கான ஒரு Lisp கிளைமொழியாகத் (dialect) தெரிகிறது. ஏதோ ஒரு வகையில் Lisp-ன் கண்டுபிடிப்பாளர்கள் ஆரம்பத்திலேயே சரியான சமநிலையை அடைந்திருப்பதாகத் தெரிகிறது, அது என்ன என்பதை நான் கண்டறிய வேண்டும்.

எனக்குத் தரவு அறிவியல் (Data Science) குறித்த நிறைய யோசனைகள் உள்ளன, அந்தத் துறைகளிலும் (குறைந்தது தனிப்பட்ட பயன்பாட்டிற்குமെങ്കിലും) பயன்படுத்தும் அளவுக்கு Clojure வேகமாகவும் சக்திவாய்ந்ததாகவும் தோன்றுகிறது. அதையும் நான் ஆராய விரும்புகிறேன், இருப்பினும் வேகமான கணக்கீடுகளுக்கு JVM எப்போதாவது GPU-வில் இயங்குமா என்று நான் வியக்கிறேன்.

நான் கண்ட ஒரே எதிர்மறையான விஷயம் JVM. Java, Oracle நிறுவனத்தால் கட்டுப்படுத்தப்படுகிறது, அது Microsoft மற்றும் Apple போன்ற ஒரு தீய நிறுவனமாகும். எனக்கு இருக்கும் ஒரே எதிர்மறை எண்ணம் இதுதான், ஆனால் விஷயங்கள் எப்படி நகர்கின்றன என்று பார்ப்போம்.

உதவி பெறுவது எங்கே?

நீங்கள் நிரலாக்கத்திற்கு (programming) புதியவர் என்றாலும் அல்லது அனுபவம் வாய்ந்த நிரலாளராக இருந்தாலும், அவ்வப்போது நீங்கள் ஏதேனும் ஒரு இடத்தில் சிக்கிக்கொள்ள வாய்ப்புள்ளது, அப்போது உங்களுக்கு உதவி தேவைப்படலாம். Clojure பயன்படுத்துபவர்களுக்கு, இந்த அத்தியாயத்தில் பட்டியலிடப்பட்டுள்ள வழிகள் உதவியாக இருக்கும்.

உள்ளூர் சமூகங்கள் (Local communities)

உங்கள் பகுதியில் உள்ள Clojure மற்றும் GNU/Linux சமூகங்களைத் தேடி, அவர்களுடன் நட்புறவை ஏற்படுத்திக் கொள்வது சிறந்தது. உங்களுக்கு அத்தகைய சமூகம் கிடைத்தால், அதில் சேர முயற்சி செய்யுங்கள். உதவி பெறுவதற்கு இதுவே சிறந்த வழியாகும். உங்களுக்கு அருகிலுள்ள குழுக்களை இங்கே தேடலாம்: https://clojure.org/community/user_groups.

Clojure இணையதளம்

உங்களிடம் நேரமும் பொறுமையும் இருந்தால், உதவி தேடுவதற்கான ஒரு நல்ல தொடக்கப் புள்ளியாக Clojure இணையதளம் இருக்கும். அதை இங்கே பார்வையிடலாம்: https://clojure.org/.

Clojure மன்றம் (Clojure forum)

அதிகாரப்பூர்வ Clojure மன்றம் இங்கே உள்ளது: https://ask.clojure.org/, இதில் இணைந்து நீங்கள் கேள்விகளைக் கேட்கத் தொடங்கலாம். எனது ஆரம்பகால சந்தேகங்களில் சில இந்த மன்றத்தின் மூலமே தீர்க்கப்பட்டன.

Clojure-க்கு மற்றொரு மன்றமும் உள்ளது: https://clojureverse.org/, இது Clojureverse என்று அழைக்கப்படுகிறது, இதுவும் மிகவும் பிரபலமானது.

Reddit

Reddit-லும் Clojure சமூகம் உள்ளது. நீங்கள் ஒரு Reddit பயனராக இருந்தால், அந்தச் சமூகத்தை இங்கே காணலாம்: https://www.reddit.com/r/Clojure/.

இந்தப் புத்தகத்தைப் பெறுவது எப்படி?

இந்தப் புத்தகத்தை நீங்கள் இங்கே பெறலாம்: https://clojure-book.gitlab.io/.

1. தேவையானவற்றை நிறுவுதல் (Installing Stuff)

இந்தப் பகுதிக்கான வீடியோவை இங்கே காணலாம்: https://yu7.in/clj-installing-stuff

1.1. Clojure

Clojure அனைத்து இயங்குதளங்களிலும் (operating systems) இயங்கும். நீங்கள் Clojure-ஐ இங்கே உள்ள இணைப்பைப் பின்பற்றி நிறுவலாம்: https://clojure.org/guides/install_clojure. நிறுவிய பிறகு, Clojure REPL-ஐச் சரிபார்க்க வேண்டிய நேரம் இது. (REPL என்றால் என்ன என்பதை அறிய, https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop-ஐப் பார்க்கவும்). உங்கள் டெர்மினலில் clj என்று தட்டச்சு செய்யவும், அப்போது உங்களுக்கு இது போன்ற ஒன்று தெரிய வேண்டும்:

$ clj
Clojure 1.10.2
user=>

இதுதான் Clojure REPL. இப்போது கீழே காட்டியபடி அதில் (println "Hello World") என்று தட்டச்சு செய்து ENTER அழுத்தவும்.

$ clj
Clojure 1.10.2
user=> (println "Hello World")

உங்களுக்கு Hello World என்று அச்சிடப்பட்டது தெரியும்:

$ clj
Clojure 1.10.2
user=> (println "Hello World")
Hello World
nil
user=>

Hello World-க்கு பிறகு ஒரு nil இருக்கும். அதைப் பற்றி அதிகம் கவலைப்பட வேண்டாம்; அது என்ன என்பதைப் பிறகு பார்ப்போம். nil-க்கு பிறகு, மீண்டும் user⇒ என்று தோன்றும், இதன் பொருள் Clojure நீங்கள் அடுத்த கட்டளையைத் தட்டச்சு செய்யக் காத்திருக்கிறது.

நீங்கள் Clojure REPL-லிருந்து வெளியேற விரும்பினால், CTRL+D அழுத்தவும். நீங்கள் மீண்டும் உங்கள் டெர்மினல் பிராம்ப்டிற்கு (terminal prompt) வந்துவிடுவீர்கள்.

1.2. Leiningen

leiningen

Leiningen என்பது ஒரு Clojure பிராஜெக்டை அமைப்பது போன்ற பணிகளைத் தானியக்கமாக்கும் (automate) ஒரு கருவியாகும். தயவுசெய்து அதன் இணையதளத்தைப் பார்வையிட்டு https://leiningen.org/ அதை நிறுவுங்கள். நிறுவிய பின், உங்கள் டெர்மினலில் lein repl என்று தட்டச்சு செய்யவும், அப்போது உங்களுக்கு இது தெரிய வேண்டும்:

$ lein repl
nREPL server started on port 64285 on host 127.0.0.1 - nrepl://127.0.0.1:64285
REPL-y 0.4.4, nREPL 0.8.3
Clojure 1.10.1
OpenJDK 64-Bit Server VM 1.8.0_152-release-1056-b12
    Docs: (doc function-name-here)
          (find-doc "part-of-name-here")
  Source: (source function-name-here)
 Javadoc: (javadoc java-object-or-class-here)
    Exit: Control+D or (exit) or (quit)
 Results: Stored in vars *1, *2, *3, an exception in *e

user=>

வெளியேற CTRL+D அழுத்தவும். எல்லாம் சரியாக உள்ளது, நீங்கள் வெற்றிகரமாக Leiningen-ஐ நிறுவிவிட்டீர்கள்.

~/.lein/profiles.clj என்ற கோப்பைத் திறக்கவும் அல்லது உருவாக்கவும், அதில் பின்வரும் உள்ளடக்கத்தைச் சேர்க்கவும்:

{:repl {:plugins [[cider/cider-nrepl "0.47.0"]]}}

1.3. IDE-ஐ configured செய்தல்

1.3.1. VSCodium-ஐ நிறுவுதல்

vscodium

நீங்கள் எந்த IDE-யையும் பயன்படுத்தலாம், ஆனால் இது எனது புத்தகம் என்பதால், VSCodium-ஐப் பயன்படுத்த நான் பரிந்துரைக்கிறேன். இந்த IDE மைக்ரோசாப்ட் உருவாக்கிய எடிட்டரை எடுத்து, அதிலுள்ள டெலிமெட்ரி (telemetry) மற்றும் கண்காணிப்புகளை நீக்கி உருவாக்கப்பட்டது. நீங்கள் இந்த IDE-யைப் பெறலாம் மற்றும் நிறுவுவதற்கான வழிமுறைகளை https://vscodium.com/-ல் காணலாம்.

1.3.2. Calva

calva

VSCodium-ல் Clojure-ஐப் பயன்படுத்த, Calva என்ற சிறந்த விரிவாக்கம் (extension) உள்ளது. இதைப் பற்றி https://calva.io-வில் நீங்கள் அறிந்து கொள்ளலாம். VSCodium extensions-க்குச் சென்று, அதைத் தேடி நிறுவுங்கள்:

searching for calva

https://calva.io/getting-started/-ல் உள்ள சிறந்த வழிகாட்டிகள் மற்றும் வீடியோக்களின் மூலம் நீங்கள் Calva-வைத் தொடங்கலாம்.

1.3.3. பயனர் அமைப்புகள் (user settings) JSON-ஐ மாற்றுதல்

VSCodium-ல், CTRL+SHIFT+P (அல்லது macOS-ல் CMD+SHIFT+P) அழுத்தி "settings json" என்று தட்டச்சு செய்து பயனர் அமைப்புகள் JSON கோப்பைத் திறக்கவும். உங்களுக்கு இது போன்ற விருப்பங்கள் தெரிய வேண்டும்:

settings json

Preferences: Open User Settings (JSON) என்பதைத் தேர்ந்தெடுத்து ENTER அழுத்தவும். உங்களுக்கு இது போன்ற உள்ளடக்கத்தைக் கொண்ட ஒரு JSON கோப்பு தெரியும்:

{
    "git.confirmSync": false
}

பின்வரும் அமைப்புகளைச் சேர்க்கவும்:

"calva.outputDestinations": {
   "evalResults": "repl-window",
   "evalOutput": "repl-window"
}

இதை JSON ஆப்ஜெக்ட்டின் இறுதியில் சேர்க்கவும், அப்போது அது இவ்வாறு தோன்றும்:

{
    "git.confirmSync": false,
    "calva.outputDestinations": {
        "evalResults": "repl-window",
        "evalOutput": "repl-window"
    }
}

இதைச் சேமிக்கவும். இப்போது நீங்கள் தயாராகிவிட்டீர்கள்.

2. ஹோஸ்டட் மொழி (Hosted Language)

இந்தப் பகுதிக்கான வீடியோவை இங்கே காணலாம்: https://yu7.in/clj-hosted-language
hosted language

Clojure ஒரு ஹோஸ்டட் மொழி (hosted language), அதாவது இது நேரடியாக ஒரு செயலி (processor) அல்லது இயங்குதளத்தில் (operating system) இயங்கும் மெஷின் கோடாக (machine code) தொகுக்கப்படுவதில்லை. Clojure ஆரம்பத்தில் ஜாவா ரன்டைம் என்விரோன்மெண்டில் (Java Runtime Environment - JRE) இயங்கும் வகையில் வடிவமைக்கப்பட்டது, இது JVM பைட்-கோடாக (bytecode) தொகுக்கப்படுகிறது. Clojure ஒரு ஹோஸ்டட் மொழி என்பதால், மேம்பாட்டாளர்கள் Clojure-ஐ ஜாவாஸ்கிரிப்ட்டிற்கு (JavaScript) தொகுக்கும் வழிகளைக் கண்டறிந்தனர், இது ClojureScript (https://clojurescript.org/) உருவாக்கத்திற்கு வழிவகுத்தது. Clojure-ஐ Common Language Runtime (https://clojure.org/about/clojureclr) - .NET மொழி குடும்பத்தின் இலக்கு சூழலுக்கும் தொகுக்க முடியும். கூடுதலாக, ClojureDart (https://github.com/Tensegritics/ClojureDart) மூலம் இது Dart மொழியிலும் தொகுக்கப்படுகிறது. இவ்வாறு, Clojure-ஐக் கற்றுக்கொள்வதன் மூலம், ஒருவர் பல தளங்களை (platforms) இலக்காகக் கொண்டு நிரலாக்கம் செய்ய முடியும்.

ஜாவாவைப் போல "ஒருமுறை எழுதுங்கள், எங்கும் இயக்குங்கள்" (Write Once, Run Anywhere - WORA) என்று Clojure உறுதியளிக்கவில்லை என்பதை கவனத்தில் கொள்க. மாறாக, வெவ்வேறு தளங்களை இலக்காகக் கொள்ளும்போது உங்கள் குறியீட்டை மாற்ற வேண்டியிருக்கலாம். இருப்பினும், அனைத்து தளங்களிலும் உங்களுக்கு ஒரே மாதிரியான Clojure தொடரியல் (syntax) கிடைக்கும்.

ஒரு வெப் டெவலப்பராக, நான் பேக்-எண்ட் (backend)-ஐ எனக்கு விருப்பமான எந்த மொழியிலும் எழுத முடியும், ஆனால் பிரண்ட்-எண்டிற்கு (frontend), நான் பெரும்பாலும் ஜாவாஸ்கிரிப்ட்டைப் பயன்படுத்த வேண்டிய கட்டாயத்தில் இருக்கிறேன். ஆனால் Clojure-ன் உதவியுடன், நான் பிரண்ட்-எண்ட் மற்றும் பேக்-எண்ட் இரண்டையும் Clojure-லேயே எழுத முடியும், இதன் மூலம் மொழிகளை மாற்றும்போது ஏற்படும் மன அழுத்தத்தைத் தவிர்க்கலாம்.

3. முதல் படிகள் (First Steps)

3.1. REPL

நான் முதன்முதலில் Clojure-ஐப் படிக்கத் தொடங்கியபோது, எனக்கு REPL-மூலம் மேம்பாடு செய்யும் முறை (REPL-driven development) அறிமுகப்படுத்தப்பட்டது. ஆரம்ப நாட்களில் அது என்னவென்று எனக்குப் புரியவில்லை; உங்களுக்கு இப்போதும் புரியவில்லை என்றாலும் கவலைப்பட வேண்டாம், காலப்போக்கில் அது புரியும். இந்தப் பகுதியில், REPL-மூலம் மேம்பாடு செய்வதன் ஒரு சிறு அறிமுகத்தைப் பார்ப்போம்.

3.1.1. டெர்மினலில் REPL (REPL in Terminal)

உங்கள் டெர்மினலில், clj என்று தட்டச்சு செய்யவும், அப்போது உங்களுக்கு இது போன்ற ஒன்று தெரிய வேண்டும்:

$ clj
Clojure 1.11.1
user=>

Clojure 1.11.1 என்பது உங்கள் கணினியில் Clojure பதிப்பு 1.11.1 நிறுவப்பட்டுள்ளது என்பதைக் குறிக்கிறது, மற்றும் user⇒ என்பது Clojure உங்கள் உள்ளீட்டிற்காகக் காத்திருக்கும் பிராம்ப்ட் (prompt) ஆகும். கீழே காட்டியபடி (println "Hello World") என்று தட்டச்சு செய்து Enter அழுத்தவும்:

$ clj
Clojure 1.11.1
user=> (println "Hello World")
Hello World
nil
user=>

உங்களுக்கு Hello World என்ற வெளியீடு தோன்றும், அதைத் தொடர்ந்து nil இருக்கும். பிறகு, மீண்டும் user⇒ என்று தோன்றும்.

இப்போது REPL-லிருந்து வெளியேற CTRL+D அழுத்தவும்.

நீங்கள் clj என்று தட்டச்சு செய்தபோது உண்மையில் என்ன நடந்தது? முதலில், Clojure உங்களுக்கு user⇒ என்று காட்டியது. நீங்கள் குறியீட்டை உள்ளிட்டு Enter அழுத்திய பிறகு, Clojure REPL அதை Read (வாசித்தது) மற்றும் Evaluated (மதிப்பீடு செய்தது), நீங்கள் Hello World என்று அச்சிட விரும்புகிறீர்கள் என்பதைத் தீர்மானித்தது, Hello World-ஐ (மற்றும் அதன் ரிட்டர்ன் மதிப்பான nil-ஐ) Print (அச்சிட்டது), பின்னர் மீண்டும் user⇒ என்ற பிராம்ப்டிற்கு Looped (திரும்பியது).

3.1.2. VSCodium-ல் REPL (REPL in VSCodium)

டெர்மினலில் REPL எவ்வாறு இயங்குகிறது என்று பார்த்தோம்; இப்போது அதை VSCodium-ல் பார்ப்போம். code என்ற பெயரில் ஒரு ஃபோல்டரை உருவாக்கி அதை VSCodium-ல் திறக்கவும். இப்போது அதில் hello_world.clj என்ற கோப்பை உருவாக்கி பின்வரும் உள்ளடக்கத்தைச் சேர்க்கவும்:

;; hello_world.clj

(println "Hello World")

இப்போது டெர்மினலில் code/ ஃபோல்டருக்குள் cd செய்து lein repl என்று தட்டச்சு செய்யவும்.

$ lein repl
nREPL server started on port 52897 on host 127.0.0.1 - nrepl://127.0.0.1:52897
REPL-y 0.4.4, nREPL 0.8.3
Clojure 1.10.1
OpenJDK 64-Bit Server VM 1.8.0_152-release-1056-b12
    Docs: (doc function-name-here)
          (find-doc "part-of-name-here")
  Source: (source function-name-here)
 Javadoc: (javadoc java-object-or-class-here)
    Exit: Control+D or (exit) or (quit)
 Results: Stored in vars *1, *2, *3, an exception in *e

user=>

lein என்பது Leiningen-ன் சுருக்கமாகும்; அதை நிறுவி சரிபார்த்ததை நீங்கள் நினைவில் வைத்திருப்பீர்கள். repl கட்டளை lein-ஐ ஒரு REPL-ஐத் தொடங்கச் சொல்கிறது. மேலே காட்டியது போன்ற வெளியீடு உங்களுக்குத் தெரிய வேண்டும். வெளியீட்டில் உள்ள nrepl://127.0.0.1:52897-ஐ கவனியுங்கள்; இதன் பொருள் ஒரு REPL சர்வர் IP 127.0.0.1 [1] மற்றும் போர்ட் (port) [2] 52897-ல் இயங்குகிறது. 127.0.0.1:52897 என்பதை குறித்துக் கொள்ளுங்கள். போர்ட் எண் உங்கள் கணினியில் மாறுபடலாம்.

connect to repl 1

VSCodium-ல், நீங்கள் GNU/Linux-ல் இருந்தால் CTRL+P அழுத்தவும், அல்லது Mac-ல் இருந்தால் command+P அழுத்தி >, என்று தட்டச்சு செய்யவும். இது VSCodium-ல் கட்டளைகளை உள்ளிட அனுமதிக்கும். இப்போது Calva: Start or connect to a Clojure REPL என்று தட்டச்சு செய்யவும். நீங்கள் தட்டச்சு செய்யும்போது, VSCodium அந்த விருப்பத்தைப் பரிந்துரைக்கும்; ENTER அழுத்தவும் அல்லது அதைக் கிளிக் செய்யவும்.

connect to repl 2

VSCodium மற்றொரு மெனுவைக் காட்டும்; அதில் Connect to a running REPL, not in your project என்பதைத் தேர்ந்தெடுக்கவும்.

connect to repl 3

விருப்பங்களிலிருந்து Generic-ஐத் தேர்ந்தெடுக்கவும். பின்னர் உங்கள் REPL இயங்கிக் கொண்டிருக்கும் IP மற்றும் போர்ட் எண்ணைக் கேட்கும்.

connect to repl 4

127.0.0.1:52897 என்ற மதிப்பை பேஸ்ட் செய்து Enter அழுத்தவும். வலது பக்கம் ஒரு Clojure REPL விண்டோ திறக்கும். இப்போது, இடதுபுறம் உள்ள hello_world.clj கோப்பில், (println "Hello World")-க்கு உள்ளே கர்சரை வைத்து ALT+ENTER அல்லது option+return அழுத்தவும்.

connect to repl 5

வலதுபுறம் உள்ள REPL பேனில் (println "Hello World")-ன் முடிவைக் காண்பீர்கள். வாழ்த்துகள், VSCodium-உடன் REPL-ஐ எவ்வாறு இணைப்பது என்பதை நீங்கள் கற்றுக்கொண்டீர்கள்.

3.2. விஷயங்களை அச்சிடுதல் (Printing Things)

இந்தப் பகுதிக்கான வீடியோவை இங்கே காணலாம் https://yu7.in/clj-first-steps-printing-things
இந்தப் பகுதிக்கு, நீங்கள் https://gitlab.com/clojure-book/code/-/raw/master/printing_things.clj-ல் உள்ள குறியீட்டைப் பயன்படுத்தலாம். உங்கள் REPL டெர்மினலில் இயங்கினாலும் அல்லது உங்கள் IDE-உடன் ஒருங்கிணைக்கப்பட்டிருந்தாலும், வரிகளை ஒவ்வொன்றாக நகலெடுத்து (copy) பேஸ்ட் (paste) செய்யவும்.

முதலில், Hello World! என்று அச்சிடுவோம். கீழே உள்ள குறியீட்டை நகலெடுத்து, பேஸ்ட் செய்து, இயக்கவும்:

(println "Hello World!")

வெளியீடு (Output)

Hello World!
nil

உங்களுக்கு Hello World! மற்றும் அடுத்த வரியில் nil என்பது தெரியும். இப்போதைக்கு nil-ஐப் புறக்கணிக்கலாம்; செயல்பாடுகளை (functions) பற்றிப் படிக்கும்போது அதை விவாதிப்போம். இந்தத் задаப்பிற்கு, நமது நோக்கம் நிறைவேறியது.

நிரலாக்கத்தில் கருத்துரைகள் (Commenting) மிக அவசியம். கருத்துரைகள் என்பது குறியீட்டைப் படிக்கும் மேம்பாட்டாளருக்கான குறிப்புகள், கணினி அவற்றை இயக்கும்போது புறக்கணிக்கிறது. Clojure-ல், செமிகோலன் ;-க்கு பிறகு வருபவை அனைத்தும் கருத்துரைகள் ஆகும். எனவே கீழே உள்ள குறியீட்டில்:

(println "Hello World!") ; இந்த உலகிற்கு வணக்கம் சொல்கிறது

வெளியீடு (Output)

Hello World!
nil

; இந்த உலகிற்கு வணக்கம் சொல்கிறது என்பது ஒரு கருத்துரை, இதை Clojure interpreter புறக்கணிக்கிறது. மேலே உள்ள குறியீட்டில், குறியீட்டுடன் ஒரே வரியில் ஒரு கருத்துரையைச் சேர்த்துள்ளோம்; வழக்கமாக, இதற்கு ஒற்றைச் செமிகோலன் ; பயன்படுத்தப்படுகிறது. நீங்கள் ஒரு முழு வரியையும் கருத்துரைக்காக ஒதுக்க விரும்பினால், கீழே காட்டியபடி இரண்டு செமிகோலன்களை ;; பயன்படுத்தவும்:

;; இந்த நிரல் இந்த உலகிற்கு வணக்கம் சொல்கிறது
(println "Hello World!")

வெளியீடு (Output)

Hello World!
nil

ஒற்றை மற்றும் இரட்டை செமிகோலன்கள் ஒரே மாதிரியாகவே செயல்படும், ஆனால் வரிக்கு இடையில் வரும் கருத்துரைகளுக்கும் (inline comments) தனி வரி கருத்துரைகளுக்கும் Clojure-ல் இதுவே நடைமுறையில் உள்ள மரபாகும்.

நாம் Hello World-ஐ அச்சிட்டோம், ஆனால் பல விஷயங்களை ஒரே நேரத்தில் அச்சிட விரும்பினால் என்ன செய்வது? println மூலம் அது எளிது: கூடுதல் ஆர்கியுமெண்டுகளை (arguments) வழங்கினால் போதும், அது இவ்வாறு அச்சிடும்:

(println "Hello World!" "Try staying cool.")

வெளியீடு (Output)

Hello World! Try staying cool.
nil

இப்போது நீங்கள் Clojure-ல் வெற்றிகரமாக ஒன்றை அச்சிட்டுவிட்டீர்கள்.

3.3. கணிதம் (Arithmetic)

இந்தப் பகுதிக்கு, நீங்கள் https://gitlab.com/clojure-book/code/-/raw/master/arithmetic.clj-ல் உள்ள குறியீட்டைப் பயன்படுத்தலாம். உங்கள் REPL டெர்மினலில் இயங்கினாலும் அல்லது உங்கள் IDE-உடன் ஒருங்கிணைக்கப்பட்டிருந்தாலும், வரிகளை ஒவ்வொன்றாக நகலெடுத்து (copy) பேஸ்ட் (paste) செய்யவும்.
இந்தப் பகுதிக்கான வீடியோவை இங்கே காணலாம்: https://yu7.in/clj-arithmetic

இந்தப் பகுதியில், Clojure-ல் கணித செயல்பாடுகளை ஆராய்வோம். முதலில், 40 மற்றும் 2 ஆகிய இரண்டு எண்களைக் கூட்டுவோம்:

(+ 40 2)

வெளியீடு (Output)

42

மேலே உள்ள வெளிப்பாட்டில், நாம் கூட்டல் + குறியீட்டைப் பயன்படுத்துகிறோம் (இது உண்மையில் Clojure-ல் ஒரு செயல்பாடு/function ஆகும்). நாம் 40 மற்றும் 2-வை இந்த செயல்பாட்டிற்கு ஆர்கியுமெண்டுகளாக வழங்குகிறோம், இது அவற்றைக்கூட்டி 42-வை வழங்குகிறது. அதேபோல், கழித்தல் - குறியீட்டிற்கு 44 மற்றும் 2-வை வழங்கினால் என்ன நடக்கும் என்று பார்ப்போம்:

(- 44 2)

வெளியீடு (Output)

42

இது 44-ல் இருந்து இரண்டாவது ஆர்கியுமெண்டான 2-வை கழித்து 42-வை வழங்குகிறது.

இப்போது, பெருக்கல் * குறியீட்டிற்கு 6 மற்றும் 7-ஐ வழங்கினால் என்ன நடக்கும் என்று பார்ப்போம்:

(* 6 7)

வெளியீடு (Output)

42

இது 6 மற்றும் 7-ன் பெருக்கற்பலனான 42-வை வழங்குகிறது.

இப்போது, வகுத்தல் / குறியீட்டிற்கு 210 மற்றும் 5-ஐ வழங்கினால் என்ன நடக்கும் என்று பார்ப்போம்:

(/ 210 5)

வெளியீடு (Output)

42

210 ஆனது 5-ஆல் வகுக்கப்பட்டு 42 என்ற முடிவைத் தருகிறது.

இப்போது, + செயல்பாட்டிற்கு இரண்டுக்கும் மேற்பட்ட எண்களை வழங்கினால் என்ன நடக்கும் என்று பார்ப்போம்:

(+ 1 2 3 4 5)

வெளியீடு (Output)

15

அனைத்து எண்களும் கூட்டப்பட்டு, அதன் கூடுதல் வழங்கப்படுகிறது.

கீழே உள்ள குறியீட்டைப் பாருங்கள்; இது எப்படி இயங்குகிறது என்று உங்களால் கண்டுபிடிக்க முடிகிறதா?

(/ (+ 1 2 3 4 5) 5)

வெளியீடு (Output)

3

(/ (+ 1 2 3 4 5) 5) என்ற வெளிப்பாட்டை கவனியுங்கள். உட்புற அடைப்புக்குறிக்குள் உள்ள (+ 1 2 3 4 5)-ஐப் பாருங்கள், இங்கு பல எண்கள் ` செயல்பாட்டிற்கு வழங்கப்படுகின்றன, இது அவற்றின் கூடுதலான 15-ஐ வழங்குகிறது. `( 1 2 3 4 5)-க்கு பதிலாக 15-ஐப் பயன்படுத்தினால், நமக்கு (/ 15 5) என்ற வெளிப்பாடு கிடைக்கிறது, இதில் 15 ஆனது 5-ஆல் வகுக்கப்பட்டு 3 என்ற முடிவைத் தருகிறது.

இப்போது, /-க்கு இரண்டுக்கும் மேற்பட்ட ஆர்கியுமெண்டுகளை வழங்குவோம். உதாரணமாக, 1, 2 மற்றும் 3-ஐ வழங்கினால் \$1/6\$ கிடைக்கும்:

(/ 1 2 3) ; இது ஒரு ரேஷியோ-வை (ratio) வழங்குகிறது, இது Clojure-ன் ஒரு தரவு வகை

வெளியீடு (Output)

1/6

மேலே உள்ள வெளியீடு ஒரு பின்னமாகும் (a ratio). தசம முடிவைப் பெற, /-க்கு வழங்கப்படும் எண்களில் குறைந்தபட்சம் ஒன்றிற்கு .0 என்பதைச் சேர்க்கவும்:

(/ 1 6.0)

வெளியீடு (Output)

0.16666666666666666
இது இப்போது உங்களுக்குத் தெளிவாக இல்லை என்றால் கவலைப்பட வேண்டாம்; நீங்கள் புத்தகத்தில் முன்னேறும்போது இது விரிவாக விளக்கப்படும்.

/-க்கு வழங்கப்படும் எண்களில் குறைந்தபட்சம் ஒன்று தசம எண்ணாக ( . குறியீட்டால் குறிக்கப்படுகிறது) இருக்கும் வரை, முடிவு பின்னமாக இல்லாமல் தசமமாக இருக்கும்:

(/ 1 2.0 3)

வெளியீடு (Output)

0.16666666666666666

இங்கேயும் அதேதான் பொருந்தும்:

(/ 1 2 3.0)

வெளியீடு (Output)

0.16666666666666666

வகுத்தலின் ஈவுத்தொகையை (quotient) பெற, நீங்கள் quot செயல்பாட்டைப் பயன்படுத்தலாம்:

(quot 14 5)

வெளியீடு (Output)

2

வகுத்தலின் மீதித்தொகையை (remainder) பெற, rem செயல்பாட்டைப் பயன்படுத்தவும்:

(rem 14 5)

வெளியீடு (Output)

4

Clojure-ஐப் பயன்படுத்தி ஒரு வட்டத்தின் பரப்பளவைக் கணக்கிடுவோம். பரப்பளவு \$pi r^2\$, இங்கு \$pi\$ என்பது தோராயமாக \$22/7\$. இதை இவ்வாறு குறியீடாக எழுதலாம்:

;; ஆரம் 7 யூனிட்கள் கொண்ட வட்டத்தின் பரப்பளவைக் கணக்கிடுவோம்
(* (/ 22 7) (* 7 7))

வெளியீடு (Output)

154N

இந்த வெளிப்பாட்டில், \$pi\$ என்பது (/ 22 7) என்றும் \$r^2\$ (இங்கு \$r\$ என்பது 7) என்பது (* 7 7) என்றும் குறிக்கப்படுகிறது. இவற்றை ஒன்றாகப் பெருக்குவது (* (/ 22 7) (* 7 7)) என்ற முடிவைத் தரும், இதன் வெளியீடு 154N.

N என்பது ஒரு BigInt-ஐக் குறிக்கிறது (மிகப்பெரிய முழு எண்களைச் சேமிக்கும் முறை). REPL-ல் (type 10N) என்று இயக்கினால் clojure.lang.BigInt என்று வரும், அதே சமயம் (type 10) என்று இயக்கினால் java.lang.Long என்று வரும்.

எண்களில் ஏதேனும் ஒன்றிற்கு .0 சேர்ப்பதன் மூலம் N-ஐ நீக்கலாம்:

(* (/ 22 7) (* 7 7.0))

வெளியீடு (Output)

154.0

\$pi\$-க்கு (/ 22 7)-க்கு பதிலாக, ஜாவாவின் Math லைப்ரரியைப் பயன்படுத்தலாம், ஏனெனில் Clojure JVM-ல் இயங்குகிறது. இது ஹோஸ்ட் பிளாட்ஃபார்ம் வழங்கும் சக்திவாய்ந்த லைப்ரரிகளைப் பயன்படுத்த Clojure-ஐ அனுமதிக்கிறது. (/ 22 7)-ஐக் கீழே காட்டியபடி Math/PI-ஆல் மாற்றலாம்:

(* Math/PI (* 7 7))

வெளியீடு (Output)

153.93804002589985

அதேபோல், \$x^y\$-ஐக் கணக்கிட, நாம் Math/pow-ஐப் பயன்படுத்தலாம். (* 7 7)-ஐ (Math/pow 7 2) என்று மாற்றினால் இவ்வாறு இருக்கும்:

(* Math/PI (Math/pow 7 2))

வெளியீடு (Output)

153.93804002589985

3.4. எண்களின் வகைகள் (Types of Numbers)

இந்தப் பகுதிக்கு, நீங்கள் https://gitlab.com/clojure-book/code/-/raw/master/types_of_numbers.clj-ல் உள்ள குறியீட்டைப் பயன்படுத்தலாம். உங்கள் REPL டெர்மினலில் இயங்கினாலும் அல்லது உங்கள் IDE-உடன் ஒருங்கிணைக்கப்பட்டிருந்தாலும், வரிகளை ஒவ்வொன்றாக நகலெடுத்து (copy) பேஸ்ட் (paste) செய்யவும்.
இந்தப் பகுதிக்கான வீடியோவை இங்கே காணலாம் https://yu7.in/clj-num-types

கணிதத்தில் வெவ்வேறு வகையான எண்கள் இருப்பது போல, கணினித் துறையிலும் வெவ்வேறு வகையான எண்கள் உள்ளன. உதாரணமாக, முழு எண்கள் Clojure-ல் Long என்று அழைக்கப்படுகின்றன. ஒரு முழு எண்ணை type செயல்பாட்டிற்கு வழங்கினால், அது அந்தத் தரவின் வகையைத் தெரிவிக்கும், அதை கீழே காணலாம்:

(type 147)

வெளியீடு (Output)

java.lang.Long

தசம மதிப்புகளைக் கொண்ட எண்கள் Clojure-ல் Double என்று அழைக்கப்படுகின்றன, அதை கீழே காணலாம்:

(type 147.67)

வெளியீடு (Output)

java.lang.Double

\$pi\$-ன் மதிப்பும் Clojure-ல் ஒரு Double ஆகும்:

(type Math/PI)

வெளியீடு (Output)

java.lang.Double

ஒரு Long (முழு எண்) மற்றொரு Long-ஆல் வகுக்கப்பட்டால், அதன் முடிவு Clojure-ல் ஒரு Ratio ஆக இருக்கும்:

(type (/ 84 32))

வெளியீடு (Output)

clojure.lang.Ratio

கீழே காட்டியபடி, Clojure-ல் (/ 84 32) என்பது தசம மதிப்பை வழங்காது, மாறாக ஒரு Ratio-வை வழங்குகிறது:

(/ 84 32)

வெளியீடு (Output)

21/8

நீங்கள் ஒரு வகை எண்ணை மற்றொரு வகையாக மாற்றலாம்; உதாரணமாக, 32 என்பது Long வகையைச் சேர்ந்தது:

(type 32)

வெளியீடு (Output)

java.lang.Long

இதை double செயல்பாட்டிற்கு வழங்குவதன் மூலம் ஒரு Double-ஆக மாற்றலாம்:

(double 32)

வெளியீடு (Output)

32.0

இப்போது அதன் வகையை நீங்கள் கேட்டால், அது Double என்று பதிலளிக்கும்:

(type (double 32))

வெளியீடு (Output)

java.lang.Double

இரண்டு Long-களை வகுக்கும்போது முடிவு Ratio-வாக வருவதைத் தவிர்க்க விரும்பினால், / செயல்பாட்டிற்கு வழங்கப்படும் ஆர்கியுமெண்டுகளில் குறைந்தபட்சம் ஒன்றை Double-ஆக மாற்றவும்:

(/ 84 (double 32))

வெளியீடு (Output)

2.625

முடிவு ஒரு Ratio-வாக இல்லாமல் Double-ஆக இருக்கும்:

(type (/ 84 (double 32)))

வெளியீடு (Output)

java.lang.Double

ஒரு Double-ன் தசமமற்ற பகுதியை மட்டும் பெற, அந்த Double-ஐ long செயல்பாட்டிற்கு வழங்கவும்:

(long 42.32)

வெளியீடு (Output)

42

நீங்கள் காண்பது போல, ஒரு Double long செயல்பாட்டிற்கு வழங்கப்படும்போது, அது Long-ஆக மாற்றப்படுகிறது:

(type (long 42.32))

வெளியீடு (Output)

java.lang.Long

3.5. சரங்கள் (Strings)

இந்தப் பகுதிக்கு, நீங்கள் https://gitlab.com/clojure-book/code/-/raw/master/strings.clj-ல் உள்ள குறியீட்டைப் பயன்படுத்தலாம். உங்கள் REPL டெர்மினலில் இயங்கினாலும் அல்லது உங்கள் IDE-உடன் ஒருங்கிணைக்கப்பட்டிருந்தாலும், வரிகளை ஒவ்வொன்றாக நகலெடுத்து (copy) பேஸ்ட் (paste) செய்யவும்.
இந்தப் பகுதிக்கான வீடியோவை இங்கே காணலாம் https://yu7.in/clj-strings

இந்தப் புத்தகத்தில் நீங்கள் எழுதிய முதல் Clojure நிரல் பெரும்பாலும் இதுவாக இருந்திருக்கும்:

(println "Hello World!")

இது திரையில் Hello World! என்று அச்சிடும். இரட்டை மேற்கோள் குறிகளுக்குள் (double quotes) அடைக்கப்பட்ட "Hello World!" என்ற உரை, எழுத்துக்களின் வரிசையாகும், இது 'சரம்' (string) என்று அழைக்கப்படுகிறது.

Clojure-ல் இரட்டை மேற்கோள் குறிகளுக்குள் அடைக்கப்பட்ட அனைத்தும் String ஆகும், அதை கீழே காணலாம்:

(type "Hello")

வெளியீடு (Output)

java.lang.String

str செயல்பாட்டைப் பயன்படுத்தி சரங்களை மற்ற தரவு வகைகளுடன் இணைக்கலாம் (concatenate):

(str "1 + 2 is " (+ 1 2))

வெளியீடு (Output)

"1 + 2 is 3"

மேலே உள்ள குறியீட்டில், (+ 1 2) ஒரு Long-ஐ வழங்குகிறது, ஆனால் அதை "1 + 2 is " என்ற சரத்துடன் str செயல்பாட்டைப் பயன்படுத்தி இணைக்க முடியும்.

count செயல்பாட்டைப் பயன்படுத்தி ஒரு சரத்தில் உள்ள எழுத்துக்களின் எண்ணிக்கையைக் கண்டறியலாம்:

(count "Hello")

வெளியீடு (Output)

5

கீழே உள்ள குறியீட்டில், str செயல்பாட்டிற்கு ஆறு ஆர்கியுமெண்டுகளை வழங்குகிறோம்: ஐந்து எண்கள் மற்றும் இறுதியாக " and so on…​" என்ற சரம்:

(str 1 2 3 4 5 " and so on...")

வெளியீடு (Output)

"12345 and so on..."

Clojure இந்த மதிப்புகளை இணைத்து ஒரு ஒற்றைச் சரத்தை வழங்குகிறது.

Clojure-ல் உள்ளமைக்கப்பட்ட ஒரு சர லைப்ரரி (string library) உள்ளது; அதன் ஆவணங்களை https://clojuredocs.org/clojure.string-ல் படிக்கலாம். ஒரு சரத்தைத் தலைகீழாக மாற்ற reverse செயல்பாட்டைப் பயன்படுத்துவோம்:

(clojure.string/reverse "Hello")

வெளியீடு (Output)

"olleH"

இப்போது, அனைத்து எழுத்துக்களையும் பெரிய எழுத்துக்களாக (uppercase) மாற்றுவோம்:

(clojure.string/upper-case "Hello")

வெளியீடு (Output)

"HELLO"

இந்தப்பகுதி சரங்களைப் பற்றிய ஒரு சிறிய அறிமுகத்தை வழங்குகிறது. நீங்கள் முன்னேறும்போது இதைப் பற்றி மேலும் அறிந்து கொள்வீர்கள். காத்திருங்கள்!

3.6. மாறிகள் (Variables)

இந்தப் பகுதிக்கு, நீங்கள் https://gitlab.com/clojure-book/code/-/raw/master/variables.clj-ல் உள்ள குறியீட்டைப் பயன்படுத்தலாம். உங்கள் REPL டெர்மினலில் இயங்கினாலும் அல்லது உங்கள் IDE-உடன் ஒருங்கிணைக்கப்பட்டிருந்தாலும், வரிகளை ஒவ்வொன்றாக நகலெடுத்து (copy) பேஸ்ட் (paste) செய்யவும்.
இந்தப் பகுதிக்கான வீடியோவை இங்கே காணலாம் https://yu7.in/clj-variables

மாறிகளை ஒரு மதிப்பைத் தாங்கிய பெட்டிகளாகக் கற்பனை செய்து கொள்ளுங்கள்; அவை குறியீட்டை மனிதர்கள் எளிதாகப் படிக்கும் வகையில் மாற்றுகின்றன. உதாரணமாக, கீழே உள்ள குறியீட்டைப் பாருங்கள்:

(def my-name "Karthik")

வெளியீடு (Output)

#'user/my-name

நான் "Karthik" என்ற சரத்தை my-name என்ற பெயரிட்ட மாறிக்கு ஒதுக்கியுள்ளேன். இப்போது, my-name-ஐ Clojure குறியீட்டில் எங்கு வேண்டுமானாலும் பயன்படுத்தலாம். ஒரு மாறியை வரையறுக்கும்போது, மூன்று முக்கிய கூறுகள் உள்ளன. முதலாவதாக, அடைப்புக்குறிகள்:

 ()

பிறகு def செயல்பாடு:

(def)

def-ஐத் தொடர்ந்து மாறியின் பெயர் வரும்; இந்த எடுத்துக்காட்டில், அது my-name:

(def my-name)

def-ன் இரண்டாவது ஆர்கியுமெண்ட் my-name-ல் சேமிக்கப்பட வேண்டிய மதிப்பு, இந்த எடுத்துக்காட்டில் அது "Karthik":

(def my-name "Karthik")

இவ்வாறுதான் நாம் ஒரு மாறியை வரையறுக்கிறோம். def மூலம் வரையறுக்கப்பட்ட மாறி பொதுவாக ஒரு உலகளாவிய மாறி (global variable) என்று அழைக்கப்படுகிறது, அதாவது அது நிரலில் எங்கு வேண்டுமானாலும் கிடைக்கும். இது உங்களுக்கு இன்னும் புரியவில்லை என்றால் கவலைப்பட வேண்டாம்.

இப்போது இந்த மாறியை இவ்வாறு அச்சிடலாம்:

(println my-name)

வெளியீடு (Output)

Karthik
nil

இங்கே, நான் my-name என்ற மாறிக் கு முன்னால் "Hello " என்பதைச் சேர்க்கிறேன், இது "Karthik" என்ற சரத்தைப் பயன்படுத்துவதற்கு சமமானது:

(println "Hello" my-name)

வெளியீடு (Output)

Hello Karthik
nil

கீழே உள்ள குறியீட்டைப் பாருங்கள்:

(def greeting (str "Hello " my-name "!"))
(println greeting)

வெளியீடு (Output)

Hello Karthik!
nil

நான் greeting என்ற மாறியை வரையறுத்து, அதற்கு (str "Hello " my-name "!")-ன் முடிவை ஒதுக்கியுள்ளேன். இது "Hello ", my-name, மற்றும் "!" ஆகிய மூன்று சரங்களை இணைத்து அதன் முடிவை greeting-ல் சேமிக்கிறது. இறுதியாக, (println greeting) மூலம் அதை அச்சிடுகிறோம்.

my-name மற்றும் greeting போன்ற மாறிகள் def கீவேர்ட் (keyword) மூலம் வரையறுக்கப்பட்டதால், அவற்றை குறியீட்டில் எங்கு வேண்டுமானாலும் பயன்படுத்தலாம். ஒரு குறிப்பிட்ட பிளாக் ()-க்குள் ஒரு உள்ளூர் மாறியை (local variable) வரையறுக்க, கீழே காட்டியபடி let-ஐப் பயன்படுத்தவும்:

(let [local-variable "something"]
  (println local-variable))

வெளியீடு (Output)

something

மேலே உள்ள குறியீட்டில், "something" என்ற மதிப்புடன் local-variable என்ற மாறியை வரையறுத்து அதை அச்சிடுகிறோம். இருப்பினும், (let …​) பிளாக்கிற்கு வெளியே, இந்த உள்ளூர் மாறி இருக்காது, அதை கீழே காணலாம்:

(println local-variable)

வெளியீடு (Output)

; Syntax error compiling at (variables.clj:16:1).
; Unable to resolve symbol: local-variable in this context

local-variable-ஐ அச்சிட முயற்சிப்பது பிழையை (error) ஏற்படுத்தும்.

3.7. கோப்பில் Clojure (Clojure in file)

இந்தப் பகுதிக்கான வீடியோவை இங்கே காணலாம் https://yu7.in/clj-in-file

இதுவரை, நீங்கள் உங்கள் குறியீட்டை REPL-ல், அதாவது உங்கள் டெர்மினலிலோ அல்லது IDE-யிலோ (VSCodium போன்றவை) இயக்கியிருப்பீர்கள். இப்போது, உங்கள் குறியீட்டை ஒரு கோப்பில் சேமித்து அதை எவ்வாறு இயக்குவது என்று பார்ப்போம். முதலில், உங்கள் டெக்ஸ்ட் எடிட்டரில் பின்வரும் பெயரில் ஒரு புதிய கோப்பை உருவாக்கவும்:

;; hello_world.clj

(println "Hello World")

குறியீட்டை hello_world.clj எனச் சேமிக்கவும். பின்னர், உங்கள் டெர்மினலில், அந்தக் கோப்பு உள்ள டைரக்டரிக்கு cd செய்து பின்வரும் கட்டளையை இயக்கவும்:

$ clj hello_world.clj

முழு நிரலும் இயங்கும், மற்றும் வெளியீடாக Hello World என்பதைக் காண்பீர்கள். அது இவ்வாறு இருக்க வேண்டும்:

$ clj hello_world.clj
WARNING: Implicit use of clojure.main with options is deprecated, use -M
Hello World

4. தரவு கட்டமைப்புகள் (Data Structures)

கிட்டதட்டு எல்லா நிரலாக்க மொழிகளும் கணினியின் RAM [3]-ல் தரவுகளைச் சேமிக்கவும், ஒழுங்கமைக்கவும் மற்றும் அணுகவும் ஏதேனும் ஒரு வழியை வழங்குகின்றன. இது கணினியின் தற்காலிக நினைவகத்தில் சேமிக்கப்படும் தரவுகளை நிர்வகிப்பதற்கான விரிவான குறியீடுகளை எழுதும் சுமையிலிருந்து நிரலாளர்களை விடுவிக்கிறது.

சரங்களும் (Strings) தரவு கட்டமைப்புகளே ஆகும். அவை கணினியின் நினைவகத்தில் எந்தவொரு எழுதப்பட்ட மொழியையும் பிரதிநிதித்துவப்படுத்த உதவுகின்றன மற்றும் அவற்றின் மீது செயல்பாடுகளைச் செய்ய உதவுகின்றன. இந்தப் பகுதியில், Clojure வழங்கும் மற்ற தரவு கட்டமைப்புகளை ஆராய்வோம், இவற்றை நீங்கள் நேரடியாகப் பயன்படுத்தலாம்.

4.1. வெக்டர்கள் (Vectors)

இந்தப் பகுதிக்கு, நீங்கள் https://gitlab.com/clojure-book/code/-/raw/master/vectors.clj-ல் உள்ள குறியீட்டைப் பயன்படுத்தலாம். உங்கள் REPL டெர்மினலில் இயங்கினாலும் அல்லது உங்கள் IDE-உடன் ஒருங்கிணைக்கப்பட்டிருந்தாலும், வரிகளை ஒவ்வொன்றாக நகலெடுத்து (copy) பேஸ்ட் (paste) செய்யவும்.
இந்தப் பகுதிக்கான வீடியோவை இங்கே காணலாம் https://yu7.in/clj-vectors

பல பிரிவுகளைக் கொண்ட ஒரு ரேக் (rack)-ஐக் கற்பனை செய்து கொள்ளுங்கள்; முதல் பிரிவுக்கு 0, இரண்டாவதுக்கு 1 என லேபிளிடப்பட்டுள்ளது. நீங்கள் இந்தப் பிரிவில் ஒன்றை வைத்துவிட்டு, அதன் பிரிவு எண்ணை (index) அறிந்திருந்தால் அதை மீண்டும் எடுத்துக்கொள்ளலாம். வெக்டர்களுக்கு இது ஒரு நிஜ உலக உதாரணம்.

கீழே உள்ள குறியீட்டைப் பாருங்கள்:

[1 2 3 4] ; ஒரு மிக எளிமையான வெக்டர்

வெளியீடு (Output)

[1 2 3 4]

Clojure-ல் ஒரு வெக்டரை இவ்வாறுதான் பிரதிநிதித்துவப்படுத்துவோம். இது தொடக்க சதுர அடைப்புக்குறியால் [ தொடங்கி, முடிவு சதுர அடைப்புக்குறியால் ] முடிகிறது; அவற்றுக்கு இடையில் இருப்பவை அனைத்தும் வெக்டரின் உறுப்புகள் (elements). மேலே உள்ள குறியீட்டில், நான்கு உறுப்புகள் உள்ளன: 1, 2, 3, மற்றும் 4.

வெக்டரை உருவாக்க மற்றொரு வழி, vector என்ற செயல்பாட்டிற்கு ஆர்கியுமெண்டுகளை வழங்குவது, அதை கீழே காணலாம்:

(vector 1 2 3)

வெளியீடு (Output)

[1 2 3]

மேலே உள்ள குறியீட்டில், நாம் 1, 2, மற்றும் 3-ஐ vector செயல்பாட்டிற்கு வழங்கினோம், நமக்கு [1 2 3] என்ற வெக்டர் கிடைத்தது.

இதுவரை எண்களைக் கொண்டு உருவாக்கப்பட்ட வெக்டர்களைப் பார்த்தோம்; உண்மையில், எந்த வகையான மதிப்பையும் கொண்டு ஒரு வெக்டரை உருவாக்கலாம்:

[1 true "Bashir"] ; பல தரவு வகைகளைக் கொண்ட வெக்டர்

வெளியீடு (Output)

[1 true "Bashir"]

மேலே உள்ள குறியீட்டில், நாம் 1 (Long), true (boolean - இதைப் பற்றி நீங்கள் விரைவில் கற்றுக்கொள்வீர்கள்), மற்றும் "Bashir" (String) ஆகிய மூன்று உறுப்புகளைக் கொண்ட ஒரு வெக்டரை உருவாக்கினோம்.

கீழே உள்ள குறியீட்டில், friends என்ற பெயரில் ஒரு மாறியை வரையறுத்து, அதில் நான்கு பெயர்களைக் கொண்ட ஒரு வெக்டரை ஒதுக்குகிறோம்:

(def friends
    ["Ram" "Bashir" "Antony" "Buddha"])

வெளியீடு (Output)

#'user/friends

இப்போது friends வெக்டரின் முதல் உறுப்பு என்ன என்று பார்ப்போம். அதற்கு, friends-ஐ first என்ற செயல்பாட்டிற்கு வழங்குகிறோம்:

(first friends)

வெளியீடு (Output)

"Ram"

இது வெக்டரின் முதல் உறுப்பை வழங்குகிறது.

rest என்ற ஒரு செயல்பாடும் உள்ளது, இது ஒரு வெக்டரை வழங்கும்போது, முதல் உறுப்பைத் தவிர்த்துவிட்டு மற்ற உறுப்புகளை வழங்குகிறது, அதை கீழே காணலாம்:

(rest friends)

வெளியீடு (Output)

("Bashir" "Antony" "Buddha")

நீங்கள் காண்பது போல, "Ram" என்பது முதல் உறுப்பு என்பதால் புறக்கணிக்கப்பட்டு, மற்றவை வழங்கப்படுகின்றன.

friends-ன் வகையைச் சரிபார்ப்போம்:

(type friends)

வெளியீடு (Output)

clojure.lang.PersistentVector

வெளியீட்டைப் பார்த்தால் அது ஒரு வெக்டர் என்பது தெளிவாகிறது, ஆனால் rest என்ன வகையை வழங்குகிறது என்று பார்ப்போம்:

(type (rest friends))

வெளியீடு (Output)

clojure.lang.PersistentVector$ChunkedSeq

இது ("Bashir" "Antony" "Buddha") (சதுர அடைப்புக்குறிக்குள் இல்லை) என்று வழங்கினாலும், அதன் வகையைத் தேடினால் அது வழங்கப்பட்ட வெக்டரின் ஒரு 'chunked sequence' என்பது தெரியவருகிறது.

nth செயல்பாட்டைப் பயன்படுத்தி வெக்டரில் உள்ள எந்தவொரு உறுப்பையும் அதன் இண்டெக்ஸ் (index) மூலம் அணுகலாம். இதன் முதல் ஆர்கியுமெண்ட் ஒரு வெக்டராகவும், இரண்டாவது அதன் இண்டெக்ஸ் எண்ணாகவும் இருக்க வேண்டும்:

(nth friends 3)

வெளியீடு (Output)

"Buddha"

friends-ன் இண்டெக்ஸ் 3-ல் உள்ள மதிப்பு "Buddha" என்ற சரமாகும்.

மூன்றாவது இண்டெக்ஸ் உறுப்பை இவ்வாறு அணுகலாம்:

(friends 3)

வெளியீடு (Output)

"Buddha"

நீங்கள் friends-ஐ ஒரு வெக்டராக வரையறுக்கும்போது, Clojure உங்களை friends-ஐ ஒரு செயல்பாடாகப் பயன்படுத்த அனுமதிக்கிறது, இது ஒரு இண்டெக்ஸை எடுத்து அந்த இடத்தில் உள்ள மதிப்பை வழங்குகிறது.

ஒரு வெக்டரின் முடிவில் ஒரு உறுப்பைச் சேர்க்க, conj செயல்பாட்டைப் பயன்படுத்தவும். இது முதல் ஆர்கியுமெண்டாக வெக்டரையும், இரண்டாவது சேர்க்க வேண்டிய மதிப்பையும் எடுத்துக்கொள்ளும்:

(conj friends "Periyaar")

வெளியீடு (Output)

["Ram" "Bashir" "Antony" "Buddha" "Periyaar"]

மேலே உள்ள குறியீட்டில், நாம் friends-ல் "Periyaar"-ஐச் சேர்த்தோம், ஆனால் இதன் பொருள் friends மாற்றப்பட்டுவிட்டது என்று அர்த்தமல்ல. நாம் friends-ஐ அச்சிட்டுப் பார்ப்போம்:

(println friends)

வெளியீடு (Output)

[Ram Bashir Antony Buddha]
nil

இது இன்னும் "Periyaar" இல்லாமல் அசல் மதிப்புகளைக் காட்டுகிறது. உண்மையில், நாம் (conj friends "Periyaar") என்று அழைக்கும்போது, Clojure ஒரு புதிய வெக்டரை உருவாக்கி அதை வழங்குகிறது.

வெக்டரின் தொடக்கத்தில் ஒரு உறுப்பைச் சேர்க்க, கீழே காட்டியபடி cons-ஐப் பயன்படுத்தவும்:

(cons "Periyaar" friends)

வெளியீடு (Output)

("Periyaar" "Ram" "Bashir" "Antony" "Buddha")

இது friends-ஐ மாற்றுகிறதா? கணினித் துறையில் உள்ள இம்முட்டபிலிட்டி (immutability) [4] என்ற கருத்தைப் பற்றி ஆராயுங்கள்.

4.1.1. பயிற்சி (Exercise)

  • ஒரு வெக்டரில் உள்ள மொத்த உறுப்புகளின் எண்ணிக்கையை எவ்வாறு பெறுவது?

  • நான் (friends 42) என்று கொடுத்தால் என்ன நடக்கும்?

4.2. பட்டியல்கள் (Lists)

இந்தப் பகுதிக்கு, நீங்கள் https://gitlab.com/clojure-book/code/-/raw/master/list.clj-ல் உள்ள குறியீட்டைப் பயன்படுத்தலாம். உங்கள் REPL டெர்மினலில் இயங்கினாலும் அல்லது உங்கள் IDE-உடன் ஒருங்கிணைக்கப்பட்டிருந்தாலும், வரிகளை ஒவ்வொன்றாக நகலெடுத்து (copy) பேஸ்ட் (paste) செய்யவும்.
இந்தப் பகுதிக்கான வீடியோவை இங்கே காணலாம் https://yu7.in/clj-lists

பட்டியல்கள் வெக்டர்களைப் போலவே இருக்கும். முதன்மையான வேறுபாடு என்னவென்றால், வெக்டர்கள் சதுர அடைப்புக்குறிகளால் [ மற்றும் ] அடைக்கப்பட்டுள்ளன, அதே சமயம் பட்டியல்கள் உருண்டையான அடைப்புக்குறிகளால் ( மற்றும் ) அடைக்கப்பட்டுள்ளன. உண்மையில், ஒரு முழு Clojure நிரலேயே ஒரு பட்டியலாகக் கருதலாம். உதாரணமாக, Clojure-ல் \$3 + 5\$-ஐக் கணக்கிட, நாம் (+ 3 5) என்று எழுதுவோம். இது உருண்டையான அடைப்புக்குறிகளால் சூழப்பட்டிருப்பதால், இது ஒரு பட்டியலாகும்.

அனைத்து Clojure குறியீடுகளும் பட்டியல்களால் ஆனவை. ஒரு பட்டியல் தொடக்க அடைப்புக்குறியால் ( தொடங்கி, பொதுவாக அழைக்க வேண்டிய ஒரு செயல்பாட்டைத் தொடர்ந்து கொண்டிருக்கும். உதாரணமாக, இரண்டு எண்களைக் கூட்ட, நாம் ` செயல்பாட்டைப் பயன்படுத்துகிறோம். இது `( எனத் தொடங்கி, செயல்பாட்டிற்கு வழங்கப்படும் ஆர்கியுமெண்டுகளைத் தொடர்ந்து—உதாரணமாக 3 மற்றும் 5—(+ 3 5 என்று அமையும். இறுதியாக, ஒரு முடிவு அடைப்புக்குறி ) பட்டியலை நிறைவு செய்கிறது: (+ 3 5), இதை Clojure interpreter இயக்க முடியும்.

கீழே உள்ள உதாரணத்தைப் பாருங்கள்:

'(1 2 3 4)

வெளியீடு (Output)

(1 2 3 4)

மேலே உள்ள உதாரணத்தில், நான்கு எண்களின் பட்டியலை உருவாக்கியுள்ளோம். இருப்பினும், நாம் (-ஆல் தொடங்கவில்லை, மாறாக ஒரு அப்போஸ்ட்ரோபி (apostrophe) மற்றும் அதைத் தொடர்ந்து ஒரு அடைப்புக்குறியால் தொடங்கி இருக்கிறோம், இது இவ்வாறு இருக்கும்: '(. இது அடைப்புக்குறிக்குள் உள்ள உள்ளடக்கத்தை இயக்க வேண்டாம் என்று Clojure-க்கு சொல்கிறது.

நீங்கள் அப்போஸ்ட்ரோபி இல்லாமல் முயற்சிக்கும்போது, 1 என்பது ஒரு செயல்பாடு என்றும் 2, 3, மற்றும் 4 ஆகியவை அதற்கு வழங்கப்பட்ட ஆர்கியுமெண்டுகள் என்றும் Clojure கருதுகிறது. பின்னர் அது அதை இயக்க முயற்சி செய்து தோல்வியடைகிறது, அதை கீழே காணலாம்:

(1 2 3 4)

வெளியீடு (Output)

; Execution error (ClassCastException) at lawyer/eval2148 (REPL:79).
; class java.lang.Long cannot be cast to class clojure.lang.IFn (java.lang.Long is in module java.base of loader 'bootstrap'; clojure.lang.IFn is in unnamed module of loader 'bootstrap')

அடைப்புக்குறிக்கு முன்னால் உள்ள அப்போஸ்ட்ரோபி, '(1 2 3 4)-ஐ இயக்க வேண்டாம், மாறாக அதை ஒரு பட்டியலாக மட்டும் கருத வேண்டும் என்று Clojure-க்கு சொல்கிறது.

list செயல்பாட்டைப் பயன்படுத்தியும் நீங்கள் ஒரு பட்டியலை உருவாக்கலாம், அதை கீழே காணலாம்:

(list 1 2 3)

வெளியீடு (Output)

(1 2 3)

மேலே, நாம் 1, 2, மற்றும் 3-ஐ list செயல்பாட்டிற்கு வழங்கினோம், நமக்கு ஒரு பட்டியல் திரும்பக் கிடைத்தது.

கீழே உள்ள குறியீட்டில், நான்கு சரங்களைக் கொண்ட ஒரு பட்டியலை உருவாக்கி அதை friends-list என்ற மாறியில் ஒதுக்குகிறோம்:

(def friends-list
    '("Ram" "Bashir" "Antony" "Buddha"))

வெளியீடு (Output)

#'user/friends-list

'("Ram" "Bashir" "Antony" "Buddha")-ல் அப்போஸ்ட்ரோபியைப் பயன்படுத்தி, பட்டியலை இயக்க வேண்டாம் என்று Clojure interpreter-க்கு சொல்கிறோம் என்பதைக் கவனியுங்கள்.

இப்போது friends-list-ல் எத்தனை உறுப்புகள் உள்ளன என்று பார்ப்போம். அதற்கு, கீழே காட்டியபடி count-ஐப் பயன்படுத்தி friends-list-ஐ அதற்கு வழங்குகிறோம்:

(count friends-list)

வெளியீடு (Output)

4

எனவே, count என்பது friends-list-ல் நான்கு உறுப்புகள் இருப்பதைக் குறிக்கிறது.

friends-list-ன் முதல் உறுப்பை எடுப்போம். அதற்கு, கீழே காட்டியபடி first செயல்பாட்டைப் பயன்படுத்துகிறோம்:

(first friends-list)

வெளியீடு (Output)

"Ram"

இப்போது friends-list-ல் முதல் உறுப்பைத் தவிர மற்ற அனைத்து உறுப்புகளையும் பெறுவோம். அதற்கு, கீழே காட்டியபடி rest செயல்பாட்டைப் பயன்படுத்துகிறோம்:

(rest friends-list)

வெளியீடு (Output)

("Bashir" "Antony" "Buddha")

இப்போது friends-list-ன் நான்காவது உறுப்பை எடுப்போம். அதற்கு, nth செயல்பாட்டைப் பயன்படுத்துகிறோம், இது முதல் ஆர்கியுமெண்டாக ஒரு பட்டியலையும், இரண்டாவது எடுக்க வேண்டிய இண்டெக்ஸையும் எடுத்துக்கொள்ளும்.

(nth friends-list 3)

வெளியீடு (Output)

"Buddha"

பட்டியலின் இண்டெக்ஸ் பூஜ்ஜியத்திலிருந்து (zero) தொடங்குவதைக் கவனியுங்கள். அதாவது, முதல் உறுப்பு 0, இரண்டாவது 1 என தொடரும்.

கீழே உள்ள உதாரணத்தில், 3 என்ற எண்ணை நேரடியாக வழங்குவதன் மூலம் friends-list-ன் நான்காவது உறுப்பை எடுக்க முயற்சிப்போம்:

(friends-list 3)

வெளியீடு (Output)

Execution error (ClassCastException) at user/eval5554 (REPL:1).
clojure.lang.PersistentList cannot be cast to clojure.lang.IFn

இது தோல்வியடைகிறது.

இப்போது, பட்டியலில் ஒரு உறுப்பைச் சேர்ப்போம். அதற்கு, conj செயல்பாட்டைப் பயன்படுத்துகிறோம். conj-ன் மூலம், உறுப்பு சேர்க்கப்பட வேண்டிய பட்டியலை முதலில் வழங்கி, அதைத் தொடர்ந்து அந்த உறுப்பை வழங்குகிறோம்:

(conj friends-list "Periyaar")

வெளியீடு (Output)

("Periyaar" "Ram" "Bashir" "Antony" "Buddha")

4.3. செட்கள் (Sets)

இந்தப் பகுதிக்கு, நீங்கள் https://gitlab.com/clojure-book/code/-/raw/master/sets.clj-ல் உள்ள குறியீட்டைப் பயன்படுத்தலாம். உங்கள் REPL டெர்மினலில் இயங்கினாலும் அல்லது உங்கள் IDE-உடன் ஒருங்கிணைக்கப்பட்டிருந்தாலும், வரிகளை ஒவ்வொன்றாக நகலெடுத்து (copy) பேஸ்ட் (paste) செய்யவும்.
இந்தப் பகுதிக்கான வீடியோவை இங்கே காணலாம் https://yu7.in/clj-sets

ஒரு செட் (set) என்பது தனித்துவமான உருப்படிகளின் தொகுப்பாகும். எந்த இரண்டு உருப்படிகளும் மீண்டும் வராத ஒரு பட்டியல் அல்லது வெக்டரை கற்பனை செய்து கொள்ளுங்கள்; அதுதான் ஒரு செட். Clojure-ல், நீங்கள் ஒரு செட்டை இவ்வாறு உருவாக்கலாம்:

#{1 2 3 4}

வெளியீடு (Output)

#{1 4 3 2}

ஒரு செட் ஹேஷ் குறியீடு மற்றும் தொடக்க and curly brace #{-ஆல் தொடங்கி, செட் உருப்படிகளைத் தொடர்ந்து, முடிவு curly brace }-ஆல் முடிகிறது.

கீழே, செட்டின் வகையை நாம் வினவுகிறோம்:

(type #{1 2 3 4})

வெளியீடு (Output)

clojure.lang.PersistentHashSet

இது clojure.lang.PersistentHashSet என்பதைத் தருகிறது, இது ஆரம்ப நிலையில் இருப்பவர்களுக்குக் குழப்பமாக இருக்கலாம். அதற்கு பதிலாக, ஒரு விஷயம் செட் தானா என்று சரிபார்க்க set? செயல்பாட்டைப் பயன்படுத்தலாம், அதை கீழே காணலாம்:

(set? #{1 2 3 4})

வெளியீடு (Output)

true

#{1 2 3 4} ஒரு செட் என்பதால், அது true என வழங்குகிறது.

ஒரு செட்டில் தனித்துவமான உறுப்புகள் மட்டுமே இருக்க முடியும். ஒரு லிட்டரல் (literal) மூலம் தனித்துவமற்ற உறுப்புகளைக் கொண்ட செட்டை உருவாக்க முயற்சித்தால், அது பிழையை ஏற்படுத்தும், அதை கீழே காணலாம்:

#{1 2 3 4 4}

வெளியீடு (Output)

Syntax error reading source at (REPL:1:13).
Duplicate key: 4

தனித்துவமான பழங்களின் பெயர்களைக் கொண்ட ஒரு செட்டை உருவாக்குவோம்:

#{"Apple" "Orange" "Mango" "Banana"}

வெளியீடு (Output)

#{"Mango" "Orange" "Apple" "Banana"}

இப்போது, நகல் உறுப்பான "Apple" உடன் இதே போன்ற ஒரு செட்டை உருவாக்க முயற்சித்தால், அது பிழையை ஏற்படுத்தும், அதை கீழே காணலாம்:

#{"Apple" "Orange" "Mango" "Banana" "Apple"}

வெளியீடு (Output)

Syntax error reading source at (REPL:1:45).
Duplicate key: Apple

பழங்களின் பெயர்களைக் கொண்ட ஒரு செட்டை உருவாக்கி அதை fruits என்ற மாறியில் ஒதுக்குவோம்:

(def fruits #{"Apple" "Orange" "Mango" "Banana"})

வெளியீடு (Output)

#'user/fruits

இப்போது fruits-ல் "Banana" உள்ளதா என்று சரிபார்க்கலாம்:

(contains? fruits "Banana")

வெளியீடு (Output)

true

fruits-ல் "Banana" இருப்பதால், அது true என வழங்குகிறது.

இப்போது fruits-ல் "Jack Fruit" உள்ளதா என்று சரிபார்ப்போம்:

(contains? fruits "Jack Fruit")

வெளியீடு (Output)

false

"Jack Fruit" என்பது fruits-ன் ஒரு பகுதியாக இல்லாததால், அது false என வழங்குகிறது.

செட்டை ஒரு செயல்பாடாகப் பயன்படுத்தியும் ஒரு உறுப்பு அதில் உள்ளதா என்று சரிபார்க்கலாம்:

(fruits "Banana")

வெளியீடு (Output)

"Banana"

மேலே உள்ள குறியீட்டில், நாம் "Banana"-வை fruits-க்கு வழங்குகிறோம், அது செட்டில் இருப்பதால் "Banana" வழங்கப்படுகிறது.

நாம் "Jack Fruit"-ஐ fruits-க்கு வழங்கினால், "Jack Fruit" செட்டில் இல்லாததால் nil வழங்கப்படுகிறது:

(fruits "Jack Fruit")

வெளியீடு (Output)

nil
சிந்திக்க ஒரு விஷயம்: (fruits "Banana") வேலை செய்கிறது என்றால், Clojure fruits என்ற செட்டை ஒரு செயல்பாடாகக் கருதுகிறதா?

இப்போது fruits-ஐ "Banana"-விற்கு வழங்குவோம்:

("Banana" fruits)

வெளியீடு (Output)

Execution error (ClassCastException) at user/eval5624 (REPL:1).
java.lang.String cannot be cast to clojure.lang.IFn

இது தோல்வியடைகிறது.

Clojure-ல் கீவேர்ட்ஸ் (keywords) எனப்படும் ஒரு சிறப்பு தரவு வகை உள்ளது. அவற்றின் தொடரியல் ஒரு கோலன் (colon) மற்றும் அதைத் தொடர்ந்து ஒரு பெயர், உதாரணமாக :<some-name>. உதாரணமாக, நிரலாக்க மொழிகளின் செட் வேண்டுமென்றால், நாம் அதை இவ்வாறு வரையறுக்கலாம்:

(def programming-languages #{:ruby :python :clojure})

வெளியீடு (Output)

#'user/programming-languages

இந்தக் கட்டுரையில், programming-languages-ல் உள்ள ஒவ்வொரு உறுப்பும் ஒரு கீவேர்ட் ஆகும்.

இப்போது programming-languages-ல் :ruby உள்ளதா என்று சரிபார்ப்போம்:

(contains? programming-languages :ruby)

வெளியீடு (Output)

true

ஆம், உள்ளது.

இப்போது அது :java-வை உள்ளடக்கியுள்ளதா என்று பார்ப்போம்:

(contains? programming-languages :java)

வெளியீடு (Output)

false

இல்லை, இல்லை.

உறுப்பை செட்டுக்கு வழங்குவதன் மூலமும் அது programming-languages-ல் உள்ளதா என்று சரிபார்க்கலாம்:

(programming-languages :ruby)

வெளியீடு (Output)

:ruby

:ruby என்பது programming-languages-ல் இருப்பதால், அது வழங்கப்படுகிறது.

:ruby ஒரு கீவேர்ட் என்பதால், சரங்கள் இல்லாத சில சிறப்புப் பண்புகளைக் கொண்டுள்ளது. "Banana" என்பது fruits-ல் உள்ளதா என்று ("Banana" fruits) மூலம் சரிபார்க்க முயன்றபோது அது தோல்வியுற்றது. இருப்பினும், இதே முறையைப் பயன்படுத்தி :ruby என்பது programming-languages-ல் உள்ளதா என்று சரிபார்ப்பது வேலை செய்கிறது:

(:ruby programming-languages)

வெளியீடு (Output)

:ruby

conj செயல்பாட்டைப் பயன்படுத்தி ஒரு செட்டில் உறுப்பைச் சேர்க்கலாம். இந்த வழக்கில், programming-languages-ல் :perl-ஐச் சேர்ப்போம்:

(conj programming-languages :perl)

வெளியீடு (Output)

#{:clojure :python :perl :ruby}

அதேபோல், disj செயல்பாட்டைப் பயன்படுத்தி ஒரு செட்டில் இருந்து உறுப்பை நீக்கலாம். programming-languages-ல் இருந்து :python-ஐ நீக்குவோம்:

(disj programming-languages :python)

வெளியீடு (Output)

#{:clojure :ruby}

தற்போதைக்கு செட்கள் பற்றிய அடிப்படைகள் இத்துடன் நிறைவடைகின்றன.

4.4. மேப்கள் (Maps)

இந்தப் பகுதிக்கு, நீங்கள் https://gitlab.com/clojure-book/code/-/raw/master/maps.clj-ல் உள்ள குறியீட்டைப் பயன்படுத்தலாம். உங்கள் REPL டெர்மினலில் இயங்கினாலும் அல்லது உங்கள் IDE-உடன் ஒருங்கிணைக்கப்பட்டிருந்தாலும், வரிகளை ஒவ்வொன்றாக நகலெடுத்து (copy) பேஸ்ட் (paste) செய்யவும்.
இந்தப் பகுதிக்கான வீடியோவை இங்கே காணலாம் https://yu7.in/clj-maps

பட்டியல்கள் மற்றும் வெக்டர்களை எண் இண்டெக்ஸுகளைக் கொண்டு அணுக முடியும், அதை பின்வரும் குறியீட்டில் காணலாம்:

(def nums ["zero" "one" "two" "three" "four" "five" "six"])

(nth nums 5)

வெளியீடு (Output)

"five"

வெளியீடாக "five" கிடைக்கிறது. ஆனால் நமது தரவுத் தொகுப்பிற்கு இண்டெக்ஸுகளாகப் பயன்படுத்த பயனர்-நட்பு வார்த்தைகளை (user-friendly words) விரும்பினால் என்ன செய்வது? இங்குதான் மேப்கள் (maps) பயன்படுகின்றன.

கீழே உள்ள குறியீட்டைப் பார்த்து, பிறகு அதைத் தட்டச்சு செய்து இயக்கவும்:

{ "name" "Bashir" "age" 12 }

வெளியீடு (Output)

{"name" "Bashir", "age" 12}

மேலே உள்ள குறியீட்டில், முதல் உறுப்புக்கு "name" என்ற கீ (key) மற்றும் "Bashir" என்ற மதிப்பும் (value), இரண்டாவது உறுப்புக்கு "age" என்ற கீ மற்றும் 12 என்ற மதிப்பும் கொண்ட ஒரு மேப்பை உருவாக்குகிறோம்.

கர்லி பிரேஸ்களைப் பயன்படுத்துவதற்குப் பதிலாக, hash-map செயல்பாட்டிற்கு கீ மற்றும் மதிப்புகளை வழங்குவதன் மூலம் ஒரு மேப்பை உருவாக்கலாம்:

(hash-map "name" "Bashir"
          "age" 20)

வெளியீடு (Output)

{"age" 20, "name" "Bashir"}

hash-map செயல்பாடு ஒற்றைப்படை வரிசை ஆர்கியுமெண்டுகளை கீகளாகவும், இரட்டைப்படை வரிசை ஆர்கியுமெண்டுகளை மதிப்புகளாகவும் எடுத்துக்கொள்கிறது. மேலே உள்ள குறியீட்டில், "name" என்பது "Bashir"-க்கு மேப் செய்யப்பட்டுள்ளது மற்றும் "age" என்ற கீ மதிப்பு 20-க்கு மேப் செய்யப்பட்டுள்ளது.

இப்போது ஒரு மேப்பை உருவாக்கி அதை friend என்ற மாறியில் ஒதுக்குவோம்:

(def friend { "name" "Bashir" "age" 12 })

வெளியீடு (Output)

#'user/friend

இப்போது get செயல்பாட்டைப் பயன்படுத்தி friend-ல் இருந்து "name" என்ற கீயுடன் தொடர்புடைய மதிப்பை மீட்டெடுக்கலாம். மேப்பை முதல் ஆர்கியுமெண்டாகவும், மீட்டெடுக்க வேண்டிய கீயை இரண்டாவது ஆர்கியுமெண்டாகவும் வழங்க வேண்டும்:

(get friend "name")

வெளியீடு (Output)

"Bashir"

மேலே உள்ள குறியீடு friend-ல் "name" என்ற கீயைத் தேடுகிறது, அதன் தொடர்புடைய மதிப்பான "Bashir" வழங்கப்படுகிறது.

மேப்பில் இருந்து ஒரு மதிப்பை மீட்டெடுக்க மற்றொரு குறுக்குவழி உள்ளது: மேப்பையே ஒரு செயல்பாடாகக் கருதி அதற்கு கீயை வழங்கவும்:

(friend "name")

வெளியீடு (Output)

"Bashir"

மேலே உள்ள உதாரணத்தில், நமக்கு friend என்ற மேப் உள்ளது; அதற்கு "name" என்ற கீயை வழங்குவதன் மூலம், மேப் செய்யப்பட்ட மதிப்பு "Bashir" கிடைக்கிறது.

"name"-ன் வகை ஒரு சரம் (string) ஆகும், அதை கீழே காணலாம்:

(type "name")

வெளியீடு (Output)

java.lang.String

Clojure-ல் கீவேர்ட்ஸும் (keywords) உள்ளன, அவை கோலன் : குறியீட்டினால் தொடங்கும். கீழே காண்பது போல, :name என்பது ஒரு கீவேர்ட்:

(type :name)

வெளியீடு (Output)

clojure.lang.Keyword

சரங்களை கீகளாகப் பயன்படுத்துவதற்குப் பதிலாக, கீவேர்ட்களைக் கொண்ட ஒரு மேப்பை உருவாக்குவோம்:

(def wise-friend {:name "Periyaar"
                  :age 90})

வெளியீடு (Output)

#'user/wise-friend

மேலே உள்ள குறியீட்டில், :name என்ற கீ "Periyaar"-க்கும், :age என்ற கீ 90-க்கும் மேப் செய்யப்பட்டுள்ளது. இந்த மேப்பை wise-friend-ல் ஒதுக்குகிறோம்.

இப்போது wise-friend-ன் :name-ஐ இவ்வாறு மீட்டெடுக்கலாம்:

(wise-friend :name)

வெளியீடு (Output)

"Periyaar"

அதேபோல், wise-friend-ன் :age-ஐ கீழே காட்டியபடி மீட்டெடுக்கலாம்:

(get wise-friend :age)

வெளியீடு (Output)

90

மேலே உள்ள உதாரணத்தில், நாம் get செயல்பாட்டைப் பயன்படுத்தி, மேப்பை முதல் ஆர்கியுமெண்டாகவும் கீயை இரண்டாவது ஆர்கியுமெண்டாகவும் வழங்கினோம்.

பின்வரும் உதாரணத்தில், (wise-friend :name) மூலம் wise-friend-ன் பெயரைப் பெற்று, அதை print செயல்பாட்டிற்கு வழங்குகிறோம், அதனுடன் "is very wise." என்ற இரண்டாவது ஆர்கியுமெண்டையும் சேர்க்கிறோம்:

(print (wise-friend :name)
       "is very wise.")

வெளியீடு (Output)

Periyaar is very wise.nil

வெளியீடு Periyaar is very wise.nil என்று வருகிறது. மேலே உள்ள உதாரணத்தில் print-க்கு பதிலாக println-ஐப் பயன்படுத்திப் பாருங்கள். நீங்கள் என்ன கவனிக்கிறீர்கள்?

wise-friend-ல் கீகளாக கீவேர்டுகளைப் பயன்படுத்தியதால், மதிப்பை இவ்வாறு மீட்டெடுக்கலாம்:

(:name wise-friend)

வெளியீடு (Output)

"Periyaar"

மேலே உள்ள குறியீட்டில், நாம் கீவேர்ட் :name-ஐ ஒரு செயல்பாடாகக் கருதி, அதற்கு wise-friend மேப்பை வழங்கி மதிப்பை மீட்டெடுக்கிறோம்.

இப்போது, wise-friend-ல் ஒரு புதிய கீ-மதிப்பு ஜோடியைச் சேர்ப்போம்:

(assoc wise-friend :belief "Rationalism")

வெளியீடு (Output)

{:name "Periyaar", :age 90, :belief "Rationalism"}

மேலே உள்ள குறியீட்டில், நாம் assoc (associate) செயல்பாட்டைப் பயன்படுத்துகிறோம். நாம் wise-friend மேப்பை வழங்கி, அதைத் தொடர்ந்து :belief என்ற கீயையும் "Rationalism" என்ற மதிப்பையும் வழங்குகிறோம். Clojure-ல், தரவு கட்டமைப்புகள் மாற்ற முடியாதவை (immutable). மேலே உள்ள செயல்பாடு {:name "Periyaar", :age 90, :belief "Rationalism"} என்ற புதிய மேப்பை வழங்குகிறது, மற்றும் நாம் அதை ஒரு மாறியில் ஒதுக்காததால், முடிவு சேமிக்கப்படவில்லை. wise-friend மாற்றப்பட்டு புதிய கீ மற்றும் மதிப்பு சேர்க்கப்பட்டது என்று நீங்கள் நினைக்கலாம். இருப்பினும், நீங்கள் wise-friend-ல் :belief-ஐ வினவினால், அது nil-ஐ வழங்கும்:

(wise-friend :belief)

வெளியீடு (Output)

nil

wise-friend-ல் உள்ள அனைத்து அசல் மதிப்புகளும் அப்படியே உள்ளன. அதன் வயதை வினவுவோம்:

(wise-friend :age)

வெளியீடு (Output)

90

இப்போது, கீழே காட்டியபடி wise-friend-ல் இருந்து ஒரு கீ-மதிப்பு ஜோடியை நீக்குவோம்:

(dissoc wise-friend :age)

வெளியீடு (Output)

{:name "Periyaar"}

மேலே உள்ள குறியீட்டில், நாம் wise-friend-ல் இருந்து :age-ஐ நீக்குகிறோம், இதன் விளைவாக :name மட்டும் கொண்ட ஒரு மேப் கிடைக்கிறது. மீண்டும் ஒருமுறை, Clojure அசல் wise-friend மேப்பை மாற்றவில்லை என்பதை நினைவில் கொள்வது அவசியம். dissoc (disassociate) செயல்பாடு wise-friend-ஐ முதல் ஆர்கியுமெண்டாகவும், நீக்கப்பட வேண்டிய கீயை இரண்டாவது ஆர்கியுமெண்டாகவும் எடுத்துக்கொண்டு, அந்த கீ இல்லாத ஒரு புதிய மேப்பை உருவாக்கி வழங்குகிறது. Clojure-ல், தரவு கட்டமைப்புகள் மாற்ற முடியாதவை (immutable).

நாம் wise-friend-ன் கீகளை வினவினால், இன்னும் :name மற்றும் :age ஆகிய இரண்டையும் காண்போம்:

(keys wise-friend)

வெளியீடு (Output)

(:name :age)

மேப்பில் சேமிக்கப்பட்டுள்ள மதிப்புகளைப் பெற, அதை vals செயல்பாட்டிற்கு வழங்கவும்:

(vals wise-friend)

வெளியீடு (Output)

("Periyaar" 90)

wise-friend-ல் :name (ஒரு கீவேர்ட்) என்ற கீ உள்ளது, மேலும் நாம் "name" (ஒரு சரம்) என்பதையும் பயன்படுத்தலாம். இதன் பொருள், ஒரே எழுத்துக்களால் ஆன கீவேர்ட் மற்றும் சரம் ஆகிய இரண்டையும் கீகளாகக் கொண்ட ஒரு மேப்பை உங்களால் உருவாக்க முடியும், அதை கீழே காணலாம்:

(assoc wise-friend "name" "Ramasamy")

வெளியீடு (Output)

{:name "Periyaar", :age 90, "name" "Ramasamy"}

"name"-ஐ ஒரு கீயாகச் சேர்த்து அதை "Ramasamy" என்ற மதிப்புடன் இணைப்பதன் மூலம், :name மற்றும் "name" ஆகிய இரண்டையும் கீகளாகக் கொண்ட ஒரு மேப்பை உருவாக்குகிறோம். இது சாத்தியமானது என்றாலும், குறியீட்டின் தெளிவுக்காக இது மிகவும் ஊக்கப்படுத்தப்படுவதில்லை.

4.5. வெக்டர்கள் மற்றும் பட்டியல்களுக்கு இடையிலான வேறுபாடு (Difference Between Vectors and Lists)

இந்தப் பகுதிக்கான வீடியோவை இங்கே காணலாம் https://yu7.in/clj-vec-list

கடந்த சில பகுதிகளைக் கவனித்திருந்தால், வெக்டர்கள் சதுர அடைப்புக்குறிகளுக்குள் [ ] பொருட்களை வைப்பதன் மூலம் உருவாக்கப்படுகின்றன என்றும், பட்டியல்கள் '( மற்றும் )-க்கு இடையில் பொருட்களை வைப்பதன் மூலம் உருவாக்கப்படுகின்றன என்றும் நீங்கள் கவனித்திருப்பீர்கள். அதைத் தவிர மற்ற அனைத்து செயல்பாடுகளும் ஒரே மாதிரியானவை. அப்படியிருக்க, உண்மையான வேறுபாடு என்ன?

வெக்டரை ஒரு ரேக் (rack) என்று கற்பனை செய்து கொள்ளுங்கள். அந்த ரேக்கின் ஒவ்வொரு பிரிவும் 0, 1, 2 என n வரை பெயரிடப்பட்டுள்ளது.

vector

கடைசியாக நிரப்பப்பட்ட பிரிவுக்கு அடுத்த பிரிவில் நீங்கள் எதையும் வைக்கலாம். கீழே உள்ள உதாரணத்தைப் பாருங்கள்:

(def numbers ["zero" "one" "two" "three" "four"])
(conj numbers "five")

வெளியீடு (Output)

["zero" "one" "two" "three" "four" "five"]

மேலே உள்ள குறியீட்டில், நாம் numbers-ல் "five"-ஐச் சேர்க்கிறோம், Clojure அதை கடைசியாகச் சேர்க்கிறது. வெக்டர் என்பது தொடர்ச்சியான இடைவெளிகளைக் கொண்ட ஒரு பெரிய ரேக் ஆகும். நினைவகத்தில் (memory) இது மிக நெருக்கமான வடிவத்தில் சேமிக்கப்பட்டுள்ளதால், Clojure ரேக்கின் எந்தப் பகுதியையும் மிக எளிதாக அணுக முடியும். எனவே, ஒரு மில்லியன் உறுப்புகளைக் கொண்ட வெக்டரில் 7,546-வது உறுப்பை அணுகுவது கூட Clojure-ல் மிக வேகமானது. ஆனால், எல்லாவற்றையும் போலவே இதிலும் ஒரு சிக்கல் உள்ளது. உங்களிடம் ஒரு மில்லியன் உறுப்புகளைக் கொண்ட வெக்டர் உள்ளது என்றும், நீங்கள் மற்றொரு உறுப்பைச் சேர்க்க விரும்புகிறீர்கள் என்றும் வைத்துக்கொள்வோம். நினைவகத்தில் அதற்கு அடுத்தபக்க இடைவெளி கிடைக்கவில்லை என்றால் அல்லது வேறு ஏதேனும் ஒன்றால் ஆக்கிரமிக்கப்பட்டிருந்தால், Clojure தொடர்ச்சியான காலியிடத்தைத் தேடி, பின்னர் அனைத்து உறுப்புகளையும் அங்கு நகர்த்தி, புதிய மதிப்பை இறுதியில் சேர்க்க வேண்டும். இவ்வாறு தரவுகளை நகர்த்தும் செயல்முறை மிகவும் செலவுமிக்கது (costly).

எனவே, இண்டெக்ஸ் மூலம் ரேண்டம் ஆக அணுகும் வேகம் உங்களுக்கு முக்கியமில்லை என்றால், உங்களுக்கு வரிசைமுறை அணுகல் (sequential access) மட்டுமே தேவைப்பட்டால், உங்கள் தொகுப்பில் அதிக மதிப்புகளைச் சேர்க்க வேண்டுமானால் மற்றும் பெரிய பட்டியல்களைச் சேமிக்க வேண்டுமானால், பட்டியலை (list) பரிசீலியுங்கள்.

list element

ஒரு பட்டியலின் உறுப்புக்கு இரண்டு பகுதிகள் உள்ளன. ஒன்று அதன் மதிப்பு, மற்றொன்று அடுத்த உறுப்பிற்கான பாயிண்டர் (pointer). எனவே, பட்டியலின் உறுப்புகள் உங்கள் கணினி நினைவகத்தில் எங்கு வேண்டுமானாலும் இருக்கலாம், அது கீழே காட்டப்பட்டுள்ளது.

list

ஒரு புதிய உறுப்பைச் சேர்ப்பது என்பது, நீங்கள் கொடுக்கும் மதிப்பை அது கொண்டிருக்கும் மற்றும் அதன் பாயிண்டர் தற்போதைய முதல் உறுப்பைக் குறிக்கும். எனவே இப்போது புதிய மதிப்பு பட்டியலின் முதல் உறுப்பாகும். இதன் குறைபாடு என்னவென்றால், ஒரு பட்டியலின் n-வது உறுப்பை அணுக விரும்பினால், அதை அடைய Clojure n-1 உறுப்புகளின் வழியாகக் கடந்து செல்ல வேண்டும். இதன் சாதகம் என்னவென்றால், உறுப்பைச் சேர்ப்பது மிகவும் எளிது.

5. Clojure ஆவணங்களைப் படித்தல் (Reading Clojure Documentation)

இந்தப் பகுதிக்கான வீடியோவை இங்கே காணலாம் https://yu7.in/clj-docs

நீங்கள் இந்தப் புத்தகத்தைப் படிக்கிறீர்கள், ஏனெனில் இது Clojure-ஐ ஆவணப்படுத்துகிறது. நிரலாக்கத்தில், ஆவணங்கள் (documentation) மிக அவசியம்; கிட்டத்தட்ட அனைத்து நிரலாக்க மொழிகளிலும் அவற்றை வழங்குவதற்கும் அணுகுவதற்கும் ஏதோ ஒரு வழி உள்ளது. Clojure-ம் அதற்கு விதிவிலக்கல்ல. இந்தப் பகுதியில், Clojure-ன் ஆவணங்களை எவ்வாறு அணுகுவது என்று பார்ப்போம்.

5.1. doc

Clojure-ல் doc என்ற செயல்பாடு உள்ளது. இதில் நீங்கள் ஏதேனும் ஒரு சரியான செயல்பாட்டின் பெயரை வழங்க வேண்டும். உதாரணமாக, நாம் +-ஐ doc-க்கு வழங்கினால், அதன் ஆவணம் அச்சிடப்படும்:

(doc +)

வெளியீடு (Output)

clojure.core/+
([] [x] [x y] [x y & more])
  Returns the sum of nums. (+) returns 0. Does not auto-promote
  longs, will throw on overflow. See also: +'
nil

மேலே உள்ள ஆவணத்திலிருந்து, + எந்த ஆர்கியுமெண்டுகளையும் பெறலாம் மற்றும் பூஜ்ஜியத்தை வழங்கும் என்பதை நாம் காணலாம்:

(+)

வெளியீடு (Output)

0

இது மேலே உள்ள வெளியீட்டின் மூலம் உறுதிப்படுத்தப்படுகிறது. மேலும், + ஒரு ஒற்றை ஆர்கியுமெண்ட்டை ஏற்றுக்கொண்டு அந்த எண்ணையே வழங்கும் என்றும் நாம் அறியலாம்:

(+ 42)

வெளியீடு (Output)

42

இதுவும் உண்மைதான். இறுதியாக, + இரண்டு அல்லது அதற்கு மேற்பட்ட ஆர்கியுமெண்ட்டுகளை ஏற்றுக்கொண்டு அவற்றின் கூடுதலை வழங்கும் என்று அறியலாம், அதை கீழே சரிபார்க்கலாம்:

(+ 40 2)

வெளியீடு (Output)

42
(+ 10 30 2)

வெளியீடு (Output)

42

5.2. find-doc

முழு Clojure ஆவண உரையையும் தேட விரும்பினால், கீழே காட்டியபடி find-doc செயல்பாட்டைப் பயன்படுத்தலாம்:

(find-doc "sum of nums")

வெளியீடு (Output)

clojure.core/+
([] [x] [x y] [x y & more])
  Returns the sum of nums. (+) returns 0. Does not auto-promote
  longs, will throw on overflow. See also: +'
-------------------------
clojure.core/+'
([] [x] [x y] [x y & more])
  Returns the sum of nums. (+') returns 0. Supports arbitrary precision.
  See also: +
nil
clj꞉user꞉>
-------------------------
clojure.core/+
([] [x] [x y] [x y & more])
  Returns the sum of nums. (+) returns 0. Does not auto-promote
  longs, will throw on overflow. See also: +'
-------------------------
clojure.core/+'
([] [x] [x y] [x y & more])
  Returns the sum of nums. (+') returns 0. Supports arbitrary precision.
  See also: +

மேலே, "sum of nums" என்ற சரத்தைக் கொண்ட ஆவணங்களைக் கொண்ட செயல்பாடுகளைக் கண்டறிந்தேன்.

5.3. apropos

பெயரில் ஒரு குறிப்பிட்ட சரத்தைக் கொண்ட செயல்பாடுகளைத் தேட விரும்பினால், கீழே காட்டியபடி apropos செயல்பாட்டைப் பயன்படுத்தவும்:

(apropos "replace")

வெளியீடு (Output)

(clojure.core/replace clojure.string/re-quote-replacement clojure.string/replace clojure.string/replace-first clojure.walk/postwalk-replace clojure.walk/prewalk-replace clojure.zip/replace)

மேலே, "replace" என்ற சரத்தைக் கொண்ட அனைத்து செயல்பாடுகளையும் கண்டறிய apropos செயல்பாட்டைப் பயன்படுத்தினேன். இப்போது, replace செயல்பாடு என்ன செய்கிறது என்பதை அறிய அதன் ஆவணத்தைச் சரிபார்ப்போம்:

(doc replace)

வெளியீடு (Output)

-------------------------
clojure.core/replace
([smap] [smap coll])
  Given a map of replacement pairs and a vector/collection, returns a
  vector/seq with any elements = a key in smap replaced with the
  corresponding val in smap.  Returns a transducer when no collection
  is provided.
nil

5.4. ஆன்லைனில் Clojure ஆவணங்கள் (Clojure docs online)

ஆவணங்களை அணுக Clojure REPL-ஐப் பயன்படுத்துவதில் உங்களுக்கு வசதி இல்லையெனில், https://clojuredocs.org/-ல் கிடைக்கும் Clojure-ன் ஆன்லைன் ஆவணங்களைப் பயன்படுத்தலாம்.

clojuredocs

கீழே உள்ள படத்தில், reduce என்று தேடுவது "reduce" என்ற வார்த்தையைக் கொண்ட அனைத்து செயல்பாடுகளையும் காட்டுகிறது.

clojuredocs 2

reduce-ஐக் கிளிக் செய்தால் அதன் ஆவணம் காண்பிக்கப்படும்:

clojuredocs 3

உலாவியில் (browser) ஆவணங்களை அணுக ClojureDocs ஒரு வசதியான வழியாகும்.

6. தர்க்கம் மற்றும் ஒப்பீடு (Logic and comparison)

இந்தப் பகுதிக்கு, நீங்கள் https://gitlab.com/clojure-book/code/-/raw/master/logic_and_comparison.clj-ல் உள்ள குறியீட்டைப் பயன்படுத்தலாம். உங்கள் REPL டெர்மினலில் இயங்கினாலும் அல்லது உங்கள் IDE-உடன் ஒருங்கிணைக்கப்பட்டிருந்தாலும், வரிகளை ஒவ்வொன்றாக நகலெடுத்து (copy) பேஸ்ட் (paste) செய்யவும்.
இந்தப் பகுதிக்கான வீடியோவை இங்கே காணலாம் https://yu7.in/clj-logic-comp

மதிப்புகளை ஒப்பிட்டுப் பார்க்க உதவும் செயல்பாடுகளை Clojure வழங்குகிறது. உதாரணமாக, இரண்டு விஷயங்கள் சமமாக இருக்கின்றனவா என்று சரிபார்க்க சமக் குறி (=) செயல்பாட்டைப் பயன்படுத்தலாம், அதை கீழே காணலாம்:

(= 1 1)

வெளியீடு (Output)

true

1 என்பது 1-க்கு சமம் என்பதால், மேலே உள்ள குறியீடு true என்பதை வழங்குகிறது. கீழே உள்ள குறியீட்டில், 1 என்பது 2-க்கு சமமல்ல, எனவே 1 மற்றும் 2 ஆர்கியுமெண்டுகளாக வழங்கப்பட்டால் = என்பது false என்பதை வழங்குகிறது:

(= 1 2)

வெளியீடு (Output)

false

= வெறும் இரண்டு ஆர்கியுமெண்டுகளை மட்டும் ஒப்பிடுவதில்லை; நீங்கள் எத்தனை ஆர்கியுமெண்டுகளை வேண்டுமானாலும் வழங்கலாம், அனைத்து ஆர்கியுமெண்டுகளும் சமமாக இருந்தால் மட்டுமே அது true என்பதை வழங்கும்.

(= 7 7 7 )

வெளியீடு (Output)

true
(= 7 8 7)

வெளியீடு (Output)

false

பெரிது (>) என்ற செயல்பாடு, வழங்கப்பட்ட முதல் ஆர்கியுமெண்ட் இரண்டாவது ஆர்கியுமெண்ட்டை விடப் பெரியதா என்று சரிபார்க்கப் பயன்படுகிறது:

(> 1 2)

வெளியீடு (Output)

false
(> 2 1)

வெளியீடு (Output)

true

இரண்டுக்கும் மேற்பட்ட ஆர்கியுமெண்டுகளை நீங்கள் வழங்கினால், அவை இறங்கு வரிசையில் (descending order) வழங்கப்பட்டுள்ளதா என்று அது சரிபார்க்கும்; அப்படியென்றால், அது true என்பதை வழங்கும்:

(> 3 2 1)

வெளியீடு (Output)

true

பெரிது அல்லது சமம் (>=) என்ற செயல்பாடு, வழங்கப்பட்ட ஆர்கியுமெண்டுகள் இரண்டாவது ஆர்கியுமெண்ட்டை விடப் பெரியதாகவோ அல்லது சமமாகவோ இருந்தால் true என்பதை வழங்கும். கீழே உள்ள குறியீட்டில், 5 என்பது 5-க்கு சமம், எனவே அது true என்பதை வழங்கும்:

(>= 5 5)

வெளியீடு (Output)

true

கீழே உள்ள உதாரணத்தில், 6 என்பது 5-ஐ விடப் பெரியது, எனவே அது true:

(>= 6 5)

வெளியீடு (Output)

true

கீழே உள்ள குறியீடு false-ஐ வழங்குகிறது, ஏனெனில் 6 என்பது 7-ஐ விடப் பெரியதல்ல, அதே சமயம் 7-க்கு சமமுமல்ல:

(>= 6 7)

வெளியீடு (Output)

false

சிறியது (<) என்ற செயல்பாடு, வழங்கப்பட்ட ஆர்கியுமெண்டுகள் ஏறு வரிசையில் (ascending order) இருந்தால் true என்பதை வழங்கும். கீழே, 1 என்பது 2-ஐ விடச் சிறியது, எனவே true வழங்கப்படுகிறது:

(< 1 2)

வெளியீடு (Output)

true

கீழே உள்ள உதாரணத்தில், 1 என்பது 2-ஐ விடச் சிறியது, மேலும் 2 என்பது 3-ஐ விடச் சிறியது, எனவே true வழங்கப்படுகிறது:

(< 1 2 3)

வெளியீடு (Output)

true

2 என்பது 1-ஐ விடச் சிறியதல்ல, எனவே கீழே உள்ள குறியீடு false-ஐ வழங்குகிறது:

(< 2 1)

வெளியீடு (Output)

false

கீழே உள்ள குறியீடு false-ஐ வழங்குகிறது, ஏனெனில் 1, 3 மற்றும் 2 ஆகியவை ஏறு வரிசையில் வழங்கப்படவில்லை:

(< 1 3 2)

வெளியீடு (Output)

false

7 என்பது 7-க்கு சமம் என்பதால் கீழே உள்ள குறியீடு true-ஐ வழங்குகிறது:

(<= 7 7)

வெளியீடு (Output)

true

7 என்பது 8-ஐ விடச் சிறியது அல்லது சமம் என்பதால் கீழே உள்ள குறியீடு true-ஐ வழங்குகிறது:

(<= 7 8)

வெளியீடு (Output)

true

8 என்பது 7-ஐ விடச் சிறியதல்ல, அதே சமயம் அதற்கு சமமுமல்ல என்பதால் கீழே உள்ள குறியீடு false-ஐ வழங்குகிறது:

(<= 8 7)

வெளியீடு (Output)

false

இப்போது தர்க்க செயல்பாடுகளைப் (logical functions) பார்ப்போம். and செயல்பாடு, வழங்கப்பட்ட அனைத்து ஆர்கியுமெண்டுகளும் true ஆக இருந்தால் மட்டுமே true-ஐ வழங்கும்:

(and true true)

வெளியீடு (Output)

true

and-க்கு வழங்கப்பட்ட ஆர்கியுமெண்டுகளில் ஏதேனும் ஒன்று false ஆக இருந்தாலும், அது false-ஐ வழங்கும்.

(and true false)

வெளியீடு (Output)

false

or செயல்பாடு, வழங்கப்பட்ட ஆர்கியுமெண்டுகளில் ஏதேனும் ஒன்று true ஆக இருந்தாலும் true-ஐ வழங்கும்:

(or true true)

வெளியீடு (Output)

true
(or true false)

வெளியீடு (Output)

true
(or false false)

வெளியீடு (Output)

false

not செயல்பாடு எளிமையாக true-ஐ false-ஆகவும், false-ஐ true-ஆகவும் மாற்றும்:

(not true)

வெளியீடு (Output)

false
(not false)

வெளியீடு (Output)

true

7. நிபந்தனைகள் மற்றும் கிளைப்படுத்துதல் (Conditions and branching)

நிரலாக்கத்தில், கிடைக்கக்கூடிய தரவு அல்லது தகவலின் அடிப்படையில் செயல்பாட்டின் ஓட்டம் (flow of execution) பெரும்பாலும் மாற வேண்டியிருக்கும். உதாரணமாக, யார் வேண்டுமானாலும் ஒரு வலைத்தளத்தின் முகப்புப் பக்கத்தைப் பார்வையிடலாம், ஆனால் ஒரு பாதுகாக்கப்பட்ட பக்கத்தை அணுக முயற்சிக்கும் பயனர், ஏற்கனவே உள்நுழையவில்லை என்றால், அவருக்கு உள்நுழைவு திரை (login screen) காட்டப்பட வேண்டும். இத்தகைய சூழ்நிலைகளில், நாம் குறிப்பிட்ட நிபந்தனைகளைச் சரிபார்த்து, அதற்கேற்ப நிரலின் ஓட்டத்தைக் கிளைப்படுத்த வேண்டும்.

இந்தப் பகுதியில், Clojure-ல் இதை எவ்வாறு செயல்படுத்துவது என்று ஆராய்வோம்.

7.1. if

இந்தப் பகுதிக்கான வீடியோவை இங்கே காணலாம் https://yu7.in/clj-if

if என்பது Clojure-ல் உள்ள அடிப்படை நிபந்தனை சரிபார்ப்பு செயல்பாடுகளில் ஒன்றாகும். ஒரு எளிய உதாரணத்தைப் பார்ப்போம். கீழே உள்ள குறியீட்டை இயக்கவும்:

;; if.clj

(let [a 5]
  (if (> a 0)
    (println a "is positive")))

வெளியீடு (Output)

5 is positive
nil

மேலே காட்டியபடி நமக்கு 5 is positive என்ற முடிவு கிடைக்கிறது. ஒரு if செயல்பாட்டில், முதல் ஆர்கியுமெண்ட் நிபந்தனை சரிபார்ப்பாகும், இங்கே அது (> a 0). இந்த நிபந்தனை சரிபார்ப்பு true என்று திரும்பினால், இரண்டாவது ஆர்கியுமெண்டான (println a "is positive") இயக்கப்படுகிறது.

இப்போது நிரலை இவ்வாறு மாற்றவும்:

;; if.clj

(let [a -5]
  (if (> a 0)
    (println a "is positive")))

வெளியீடு (Output)

nil

மேலே உள்ள குறியீட்டில், a என்பது -5 என்பதால் (> a 0) தோல்வியடைகிறது. கோட்பாட்டு ரீதியாக, இப்போது if-க்கு வழங்கப்பட்ட மூன்றாவது ஆர்கியுமெண்ட் இயங்க வேண்டும். இங்கு முதல் ஆர்கியுமெண்ட் (> a 0) மற்றும் இரண்டாவது ஆர்கியுமெண்ட் (println a "is positive"). மூன்றாவது ஆர்கியுமெண்ட் எதுவும் இல்லை, எனவே nil திரும்பக் கிடைக்கிறது.

இப்போது அதற்கு மூன்றாவது ஆர்கியுமெண்ட்டை வழங்குவோம், கீழே உள்ள குறியீட்டைப் பார்த்து இயக்கவும்:

;; if_else.clj

(let [a -5]
  (if (> a 0)
    (println a "is positive")
    (println a "is negative")))

வெளியீடு (Output)

-5 is negative
nil

மேலே உள்ள குறியீட்டில் a என்பது -5 என்பதால் (> a 0) தோல்வியடைகிறது. எனவே மூன்றாவது ஆர்கியுமெண்ட் இயங்க வேண்டும். இந்த வழக்கில், மூன்றாவது ஆர்கியுமெண்டான (println a "is negative") இயக்கப்பட்டு -5 is negative என்று அச்சிடப்படுகிறது.

கீழே உள்ள குறியீட்டைப் பார்த்து இயக்கவும்.

;; if_nested.clj

(let [a 5]
  (if (> a 0)
    (println a "is positive")
    (if (< a 0)
      (println a "is negative")
      (println a "is neither positive nor negative"))))

வெளியீடு (Output)

5 is positive
nil

மேலே உள்ள குறியீட்டில் (> a 0) என்பது true என்பதால், முதல் ஆர்கியுமெண்டான (println a "is positive") இயக்கப்பட்டு மேலே உள்ள வெளியீடு அச்சிடப்படுகிறது.

இப்போது a-வை -5 என மாற்றி என்ன நடக்கிறது என்று பார்ப்போம்.

;; if_nested.clj

(let [a -5]
  (if (> a 0)
    (println a "is positive")
    (if (< a 0)
      (println a "is negative")
      (println a "is neither positive nor negative"))))

வெளியீடு (Output)

-5 is negative
nil

எனவே if-க்கு வழங்கப்பட்ட இரண்டாவது ஆர்கியுமெண்ட் இதுவாகும்:

(if (< a 0)
      (println a "is negative")
      (println a "is neither positive nor negative"))

இந்த ஆர்கியுமெண்டிற்குள்ளும் ஒரு if உள்ளது, இதை 'nested if' என்று அழைக்கலாம். இங்கே வழங்கப்பட்ட நிபந்தனையான (< a 0) உண்மையாகிறது, எனவே nested if-க்கு வழங்கப்பட்ட இரண்டாவது ஆர்கியுமெண்டான (println a "is negative") இயக்கப்பட்டு -5 is negative என்று அச்சிடப்படுகிறது.

இப்போது a பூஜ்ஜியமாக இருந்தால் என்னவாகும்? மீண்டும் இது டாப் லெவல் if-ன் இரண்டாவது ஆர்கியுமெண்டிற்கு வரும், அது:

(if (< a 0)
      (println a "is negative")
      (println a "is neither positive nor negative"))

மேலே உள்ள குறியீட்டில் (< a 0) தோல்வியடைகிறது, மற்றும் nested if-க்கு வழங்கப்பட்ட மூன்றாவது ஆர்கியுமெண்டான (println a "is neither positive nor negative") இயக்கப்படுகிறது. அதன் செயல்பாட்டை கீழே காணலாம்:

;; if_nested.clj

(let [a 0]
  (if (> a 0)
    (println a "is positive")
    (if (< a 0)
      (println a "is negative")
      (println a "is neither positive nor negative"))))

வெளியீடு (Output)

0 is neither positive nor negative
nil

if செயல்பாடு 3 ஆர்கியுமெண்டுகளை மட்டுமே ஏற்க முடியும். முதலாவது நிபந்தனை, நிபந்தனை உண்மையாக இருக்கும்போது இரண்டாவது ஆர்கியுமெண்ட் இயங்கும், நிபந்தனை தோல்வியடையும் போது மூன்றாவது ஆர்கியுமெண்ட் இயங்கும். நிபந்தனை உண்மையாகவோ அல்லது தோல்வியடைந்தோ இருக்கும்போது ஒன்றுக்கு மேற்பட்ட கூற்றுகளை (statements) இயக்க விரும்பினால், அவற்றை கீழே காட்டியபடி do-வில் இணைக்கவும்:

;; if_multiple_statements_in_branch.clj

(let [a 5]
  (if (> a 0)

    (do
      (println a "is positive")
      (println "There are infinite positive numbers"))

    (do
      (println a "is negative")
      (println "There are infinite negative numbers"))))

வெளியீடு (Output)

5 is positive
There are infinite positive numbers
nil

இப்போது a-வை எதிர்மறையாக மாற்றி இயக்கவும்:

;; if_multiple_statements_in_branch.clj

(let [a -5]
  (if (> a 0)

    (do
      (println a "is positive")
      (println "There are infinite positive numbers"))

    (do
      (println a "is negative")
      (println "There are infinite negative numbers"))))

வெளியீடு (Output)

-5 is negative
There are infinite negative numbers
nil

7.2. when

இந்தப் பகுதிக்கான வீடியோவை இங்கே காணலாம் https://yu7.in/clj-when

if செயல்பாட்டின் முதல் ஆர்கியுமெண்டாக நிபந்தனையை, இரண்டாவது ஆர்கியுமெண்டாக நிபந்தனை true ஆக இருக்கும்போது செய்ய வேண்டியதை, மற்றும் மூன்றாவது ஆர்கியுமெண்டாக நிபந்தனை false ஆக இருக்கும்போது செய்ய வேண்டியதை ஏற்கிறது. மூன்றாவது ஆர்கியுமெண்ட் இல்லாவிட்டாலும் if இயங்கும், ஆனால் ஒரு நிபந்தனை true ஆக இருக்கும்போது பல கூற்றுகளை இயக்க விரும்பினால், கீழே காட்டியபடி when-ஐப் பயன்படுத்தலாம்:

;; when.clj

(let [print-something true]
  (when print-something
    (println "I print something.")
    (println "I print other things too.")))

வெளியீடு (Output)

I print something.
I print other things too.
nil

மேலே உள்ள நிரலைத் தட்டச்சு செய்து இயக்கவும். print-something என்பது true ஆக இருக்கும்போது, பின்வரும் கூற்றுகள்:

(println "I print something.")
(println "I print other things too.")

இந்த வடிவத்தில்:

(when print-something
    (println "I print something.")
    (println "I print other things too."))

இயக்கப்பட்டு, மேலே உள்ள வெளியீடு கிடைக்கிறது.

இப்போது print-something-ஐ false என மாற்றி குறியீட்டை இயக்கவும்:

;; when.clj

(let [print-something false]
  (when print-something
    (println "I print something.")
    (println "I print other things too.")))

வெளியீடு (Output)

nil

எதுவும் அச்சிடப்படவில்லை. எனவே when-க்கு வழங்கப்பட்ட முதல் ஆர்கியுமெண்ட் true ஆக இருந்தால், அதன் வடிவத்தில் அடைக்கப்பட்ட அனைத்து கூற்றுகளும் இயக்கப்படும், இல்லையெனில் எதுவும் நடக்காது.

7.3. cond

பல நிபந்தனைகளைச் சரிபார்த்து, எந்த நிபந்தனை பூர்த்தியடைகிறது என்பதன் அடிப்படையில் வெவ்வேறு குறியீடுகளை இயக்க விரும்பினால் என்ன செய்வது? அதற்கு cond (conditional என்பதன் சுருக்கம்) பயன்படுகிறது. கீழே உள்ள உதாரணத்தைப் பார்த்து இயக்க முயற்சி செய்யுங்கள்:

;; cond.clj

(let [number 5]
  (cond
    (> number 0) (println number "is positive.")
    (< number 0) (println number "is negative.")
    :else (println number "is neither positive nor negative.")))

வெளியீடு (Output)

5 is positive.
nil

நாம் let பைண்டிங் மூலம் number-ஐ 5 என்று அமைத்ததால், அது 5 is positive என்று அச்சிடுகிறது. cond வடிவம் இவ்வாறு கட்டமைக்கப்பட்டுள்ளது:

(cond
    (> number 0) (println number "is positive.")
    (< number 0) (println number "is negative.")
    :else (println number "is neither positive nor negative."))

இந்த குறியீட்டில், number என்பது 0-வை விடப் பெரியது என்பதால், (> number 0) என்ற நிபந்தனை பூர்த்தியடைகிறது, மற்றும் அதனுடன் தொடர்புடைய வெளிப்பாடான (println number "is positive.") இயக்கப்படுகிறது.

number-ஐ ஒரு எதிர்மறை மதிப்பாக மாற்றி, குறியீட்டை இயக்கி முடிவைக் கவனியுங்கள். முந்தைய நிபந்தனைகள் எதுவுமே பூர்த்தியடையவில்லை என்றால், :else பகுதி இயக்கப்படும்:

:else (println number "is neither positive nor negative.")

number-ஐ 0 என்று அமைத்து குறியீட்டை இயக்கி முடிவைப் பாருங்கள்.

இப்போது, பின்வரும் குறியீட்டை இயக்கி அதன் செயல்பாட்டை ஆய்வு செய்யுங்கள்:

(let [number 5]
  (cond
    (> number 0) (println number "is positive.")
    (< number 0) (println number "is negative.")))

number-ஐ 0 என்று அமைத்து, அதை இயக்கி என்ன நடக்கிறது என்று கவனியுங்கள்.

7.4. case

கீழே case-க்கான ஒரு உதாரணம் உள்ளது, அதைத் தட்டச்சு செய்து இயக்கவும்.

;; case.clj

(let [num 20]
  (case num
    1 "one"
    2 "two"
    3 "three"
    4 "four"
    5 "five"
    "I don't know"))

வெளியீடு (Output)

"I don't know"

நமக்கு "I don’t know" கிடைக்கிறது, அப்படியென்றால் என்ன நடந்தது? let [num 20] மூலம் num-ஐ 20 என அமைத்துள்ளோம், பின்னர் num-ஐ இவ்வாறு case-க்கு வழங்குகிறோம்:

(case num
  ........)

எனவே case ஆனது num-ன் மதிப்பைச் சரிபார்க்கிறது. num என்பது 1 அல்ல என்பதால், 1 "one" என்ற கூற்று கவனிக்கப்படுவதில்லை.

(case num
    1 "one"
    ........)

அதேபோல், num என்பது 2, 3, 4, அல்லது 5 அல்ல, எனவே கீழே உள்ள அனைத்து கூற்றுகளும் கவனிக்கப்படுவதில்லை.

(case num
  1 "one"
  2 "two"
  3 "three"
  4 "four"
  5 "five"
  ........)

இறுதியாக, இயல்பான (default) நிலையான "I don’t know" இவ்வாறு இயக்கப்படுகிறது:

(case num
    ........
    "I don't know")

அது ரிட்டர்ன் செய்யப்படுகிறது.

ஒரு பயிற்சியாக, "I don’t know" என்பதை நீக்கிவிட்டு குறியீட்டை இயக்கவும். உங்களுக்கு என்ன கிடைக்கிறது? num-ஐ 4 என மாற்றி குறியீட்டை இயக்கவும். உங்களுக்கு என்ன கிடைக்கிறது? அதை எப்படி விளக்குவீர்கள்?

num என்பது 4 ஆகவும், "I don’t know" நீக்கப்பட்டும் இருக்கும்போது, அது முக்கியமா? ஏன்?

num என்பது 20 ஆகவும், "I don’t know" நீக்கப்பட்டும் இருக்கும்போது, அது முக்கியமா? ஏன்?

8. லூப்கள் (Loops)

இந்தப் பகுதிக்கு, நீங்கள் https://gitlab.com/clojure-book/code/-/raw/master/loops.clj-ல் உள்ள குறியீட்டைப் பயன்படுத்தலாம். உங்கள் REPL டெர்மினலில் இயங்கினாலும் அல்லது உங்கள் IDE-உடன் ஒருங்கிணைக்கப்பட்டிருந்தாலும், வரிகளை ஒவ்வொன்றாக நகலெடுத்து (copy) பேஸ்ட் (paste) செய்யவும்.
இந்தப் பகுதிக்கான வீடியோவை இங்கே காணலாம் https://yu7.in/clj-loops

நிரலாக்கத்தில், ஒரு செயலை மீண்டும் மீண்டும் செய்ய வேண்டியிருக்கும்போது, லூப்களைப் பயன்படுத்துகிறோம். இந்தப் பகுதியில் அவற்றைப் பற்றி ஆராய்வோம்.

Clojure-ல் மறுசெய்கை (repetition) முறை இம்பரேட்டிவ் (imperative) மொழிகளை விட வித்தியாசமானது. இதில் பாரம்பரியமான 'for' அல்லது 'while' லூப்கள் அதே அர்த்தத்தில் இல்லை, ஆனால் அதற்குப் பதிலாக சக்திவாய்ந்த மாற்றுகளை வழங்குகிறது. இந்தப் பகுதி அந்த மாற்றுகளை ஆராய்கிறது.

8.1. for

நாம் முதலில் பார்க்கப்போகும் அமைப்பு for ஆகும். பின்வரும் குறியீட்டைத் தட்டச்சு செய்து இயக்கவும்:

(let [nums [1 2 3 4]]
  (for [num nums]
       (* num 10)))

வெளியீடு (Output)

(10 20 30 40)

இது எவ்வாறு செயல்படுகிறது என்று பார்ப்போம்.

(let [nums [1 2 3 4]])

இந்த வெளிப்பாடு [1 2 3 4] என்பதை nums என்ற குறியீட்டுடன் இணைக்கிறது. பின்னர் நாம் for-ஐ இவ்வாறு பயன்படுத்துகிறோம்:

(let [nums [1 2 3 4]]
  (for [num nums]
    ;; லூப் உடல் (loop body)
       ))

(for [num nums] …​) என்ற வெளிப்பாட்டில், [num nums] பகுதி பைண்டிங்கை வரையறுக்கிறது. இதன் உடல் (body) நான்கு முறை இயக்கப்படும். முதல் முறை, nums-ல் உள்ள முதல் மதிப்பான 1, num-உடன் இணைக்கப்படும்; இரண்டாவது முறை, 2 ஆனது num-உடன் இணைக்கப்படும், இப்படியே தொடரும்.

உடலில், நாம் num-ஐ 10-ஆல் பெருக்குகிறோம். கீழே காட்டியபடி (* num 10)-ஐ லூப் உடலில் வைப்போம்:

(let [nums [1 2 3 4]]
  (for [num nums]
       (* num 10)))

for இவ்வாறு இயங்குகிறது: முதல் முறை, num என்பது 1 மற்றும் அதன் உடல் 10-ஐ வழங்குகிறது. for இதை ஒரு வரிசையாக (sequence) சேகரிக்கிறது. அடுத்த முறை, num என்பது 2 மற்றும் உடல் 20-ஐ வழங்குகிறது, அது வரிசையில் சேர்க்கப்படுகிறது. அனைத்து உறுப்புகளும் செயலாக்கப்படும் வரை இது தொடர்கிறது, இறுதியாக for முழு வரிசையையும் வழங்குகிறது: (10 20 30 40).

இப்போது பல வரிசைகளுடன் (multiple sequences) for-ஐப் பயன்படுத்துவோம். பின்வரும் நிரலைத் தட்டச்சு செய்து இயக்கவும்:

(def colors ["red" "blue" "green" "yellow"])

(def shapes ["square" "circle" "triangle" "rectangle"])

(for [color colors
      shape shapes]
  (str color " " shape))

வெளியீடு (Output)

("red square" "red circle" "red triangle" "red rectangle" "blue square" "blue circle" "blue triangle" "blue rectangle" "green square" "green circle" "green triangle" "green rectangle" "yellow square" "yellow circle" "yellow triangle" "yellow rectangle")

இந்த வெளிப்பாட்டில்:

(def colors ["red" "blue" "green" "yellow"])

நான்கு வண்ணங்களைக் கொண்ட colors என்ற வெக்டரை வரையறுக்கிறோம். அதேபோல்:

(def shapes ["square" "circle" "triangle" "rectangle"])

நான்கு வடிவங்களைக் கொண்ட shapes என்ற வெக்டரை வரையறுக்கிறோம். இப்போது ஒவ்வொரு வண்ணத்தையும் ஒவ்வொரு வடிவத்துடனும் இணைக்க for-ஐப் பயன்படுத்தலாம். இதைப்பாருங்கள்:

(for [color colors]
  ;; லூப் உடல்
  )

மேலே உள்ள குறியீட்டில், for ஆனது colors-ல் உள்ள ஒவ்வொரு உருப்படியையும் ஒவ்வொன்றாக அணுகும். முதல் முறை, color என்பது "red"-ஆகவும், கடைசி முறை "yellow"-ஆகவும் இருக்கும்.

இவற்றை shapes-உடன் இணைக்க, shape-க்கான மற்றொரு பைண்டிங்கைச் சேர்க்கிறோம்:

(for [color colors
      shape shapes]
  ;; லூப் உடல்
  )

இது இவ்வாறு இயங்குகிறது: முதலில், "red" என்பது color-உடனும், "square" என்பது shape-உடனும் இணைக்கப்படும். பின்னர் உடல் இயங்கி, அவற்றை str மூலம் இணைக்கிறது. முழுமையான குறியீடு இதோ:

(for [color colors
      shape shapes]
  (str color " " shape))

முதல் முறை, "red", ஒரு இடைவெளி, மற்றும் "square" இணைக்கப்பட்டு, for ஆனது "red square"-ஐ சேகரிக்கிறது. பின்னர், shape அடுத்த மதிப்பான "circle"-ஐ எடுத்துக்கொண்டு "red circle"-ஐ உருவாக்குகிறது. color அடுத்த மதிப்பான "blue"-க்கு நகர்வதற்கு முன், அனைத்து வடிவங்களுக்கும் இந்த செயல்முறை தொடர்கிறது. இறுதியில், for பின்வரும் வரிசையை வழங்குகிறது:

("red square" "red circle" "red triangle" "red rectangle" "blue square" "blue circle" "blue triangle" "blue rectangle" "green square" "green circle" "green triangle" "green rectangle" "yellow square" "yellow circle" "yellow triangle" "yellow rectangle")

8.2. doseq

doseq அதன் இட்டரேஷன் (iteration) தொடரியலில் for-ஐப் போலவே இருக்கும், ஆனால் இது சைடு எஃபெக்ட்களுக்காக (side effects) வடிவமைக்கப்பட்டுள்ளது மற்றும் எப்போதும் nil-ஐ மட்டுமே வழங்குகிறது. கீழே உள்ள குறியீட்டைப் பாருங்கள். for-ஐப் போலல்லாமல், இது முடிவுகளின் வரிசையை வழங்காது. மதிப்பைத் திருப்பிக் கொடுக்காமல் ஒரு செயலை மட்டும் செய்ய விரும்பினால் doseq-ஐப் பயன்படுத்தவும்.

கன்சோலில் அச்சிடுதல், கோப்பில் எழுதுதல், மின்னஞ்சல் அனுப்புதல் அல்லது தரவுத்தளத்தைப் புதுப்பித்தல் போன்ற சைடு எஃபெக்ட்களை உருவாக்கும் மீண்டும் மீண்டும் செய்யப்படும் செயல்களுக்கு doseq பயன்படுத்தப்படுகிறது.
(let [nums [1 2 3 4]]
  (doseq [num nums]
    (* num 10)))

வெளியீடு (Output)

nil

doseq என்பது nil-ஐ வழங்கினாலும், லூப் உடலுக்குள் நீங்கள் செயல்களைச் செய்யலாம். உதாரணமாக, பெருக்கலின் முடிவை அச்சிடலாம்:

(let [nums [1 2 3 4]]
  (doseq [num nums]
    (println (* num 10))))

வெளியீடு (Output)

10
20
30
40
nil

நிஜ உலகச் சூழலில், ரிட்டர்ன் மதிப்பு தேவையில்லாத பெறுநர்களின் பட்டியலுக்கு மின்னஞ்சல்களை அனுப்ப இதைப்பயன்படுத்தலாம்.

8.3. loop

இறுதியாக, loop-ஐப் பார்ப்போம். பின்வரும் நிரலைத் தட்டச்சு செய்து இயக்கவும்:

(loop [x 1]
  (when (<= x 5)
    (println x)
    (recur (inc x))))

வெளியீடு (Output)

1
2
3
4
5
nil

இது எவ்வாறு செயல்படுகிறது. முதலில், நாம் loop அமைப்பை வரையறுக்கிறோம்:

(loop )

இப்போது, x என்ற பைண்டிங்கை 1 எனத் தொடங்குகிறோம்:

(loop [x 1])

1 முதல் 5 வரையிலான எண்களை அச்சிட, லூப்பை நிறுத்த ஒரு நிபந்தனை தேவை. x என்பது 5-க்கு குறைவாகவோ அல்லது சமமாகவோ இருக்கிறதா என்று சரிபார்க்க when-ஐப் பயன்படுத்துகிறோம்:

(loop [x 1]
  (when (<= x 5)
    ))

loop-ன் முதல் பகுதி பைண்டிங்கான [x 1], அதைத் தொடர்ந்து நிபந்தனை சரிபார்ப்பைக் கொண்ட உடல் உள்ளது:

(when (<= x 5)
    )

x என்பது 5-க்கு குறைவாகவோ அல்லது சமமாகவோ இருக்கும்போதெல்லாம், when வெளிப்பாட்டின் உடல் இயக்கப்படும்.

இப்போது x-ன் தற்போதைய மதிப்பை அச்சிட println-ஐச் சேர்க்கிறோம்:

(loop [x 1]
  (when (<= x 5)
    (println x)))

லூப்பைத் தொடர, x-ன் புதுப்பிக்கப்பட்ட மதிப்புடன் loop-ன் தொடக்கத்திற்குத் திரும்ப recur-ஐப் பயன்படுத்துகிறோம். when பிளாக்கின் இறுதி வெளிப்பாடாக (recur (inc x))-ஐ வழங்குகிறோம்:

(loop [x 1]
  (when (<= x 5)
    (println x)
    (recur (inc x))))

இப்போது loop மீண்டும் தொடங்குகிறது, ஆனால் இந்த முறை x என்பது 2. (⇐ x 5) இன்னும் உண்மையாக இருப்பதால், 2 அச்சிடப்படுகிறது. x என்பது 6 ஆகும் வரை இது தொடர்கிறது, அப்போது (⇐ x 5) தோல்வியடைகிறது, மற்றும் லூப் முடிவடைகிறது.

8.4. dotimes

இந்தப் பகுதிக்கான வீடியோவை இங்கே காணலாம் https://yu7.in/clj-dotimes

ஒரு செயல் எத்தனை முறை மீண்டும் செய்யப்பட வேண்டும் என்பது உங்களுக்கு முன்கூட்டியே தெரிந்தால், நீங்கள் dotimes-ஐப் பயன்படுத்தலாம். ஒரு உதாரணத்தைப் பார்ப்போம்.

நாம் dotimes-ல் தொடங்குகிறோம்:

(dotimes )

கவுண்டர் (counter) மற்றும் இட்டரேஷன்களின் எண்ணிக்கையைக் கொண்ட ஒரு வெக்டரை வழங்குகிறோம்:

(dotimes [])

ஒரு செயலை ஐந்து முறை மீண்டும் செய்ய, 5 என்று குறிப்பிடுகிறோம்:

(dotimes [5])

தற்போதைய இட்டரேஷன் எண்ணிக்கையைப் பயன்படுத்த, அதை x போன்ற ஒரு மாறியில் இணைக்கிறோம்:

(dotimes [x 5])

இப்போது x-ன் மதிப்பை அச்சிடுகிறோம்:

(dotimes [x 5]
  (println x))

மேலே உள்ள குறியீட்டை இயக்கினால் பின்வரும் முடிவு கிடைக்கும்:

0
1
2
3
4

பெரும்பாலான நிரலாக்க மொழிகளில், எண்ணுதல் 0-வில் தொடங்கும். அதனால்தான் வெளியீடு 0-வில் தொடங்குகிறது. 0 முதல் 4 வரை ஐந்து தனித்துவமான எண்கள் உள்ளன, அதாவது உடல் ஐந்து முறை இயக்கப்பட்டது.

இதோ மற்றொரு உதாரணம்:

(dotimes [_ 5]
  (println "Hello World!"))

இந்தக் குறியீட்டை இயக்கினால் பின்வரும் வெளியீடு கிடைக்கும்:

Hello World!
Hello World!
Hello World!
Hello World!
Hello World!

நீங்கள் காண்பது போல, "Hello World!" ஐந்து முறை அச்சிடப்படுகிறது. x போன்ற மாறியிற்குப் பதிலாக ஏன் அண்டர்ஸ்கோர் _ பயன்படுத்தினோம் என்று நீங்கள் நினைக்கலாம். மரபாக, லூப் உடலிலும் இட்டரேஷன் கவுண்டர் தேவைப்படாதபோது அண்டர்ஸ்கோர் பயன்படுத்தப்படுகிறது.

9. செயல்பாடுகள் (Functions)

இந்தப் பகுதிக்கான வீடியோவை இங்கே காணலாம் https://yu7.in/clj-st-fun

முன்னேற்றத்திற்கும் சிறப்பிற்கும் அடிப்படையே நுணுக்கப்படுத்துதல் (Abstraction) ஆகும். மைக்கேல் ஃபாரடே மின்சாரத்திற்கும் காந்தத்திற்கும் இடையிலான தொடர்பைக் கண்டுபிடித்தார், ஆனால் நான் காரை ஓட்டும்போது அதைப்பற்றி யோசிப்பதில்லை. அனைத்தும் ஸ்டீயரிங் வீல் மற்றும் பெடல்களுக்குப் பின்னால் நுணுக்கப்படுத்தப்பட்டுள்ளன. இந்த நுணுக்கப்படுத்துதல் மிகவும் முக்கியமானது என்பதால், கிட்டத்தட்ட எல்லா நிரலாக்க மொழிகளும் சிக்கல்களை மறைத்து எளிமைப்படுத்த ஒரு வழியை வழங்குகின்றன.

Clojure-ல் நீங்கள் விஷயங்களை நுணுக்கப்படுத்தப் பயன்படுத்தும் வழிதான் செயல்பாடுகள் (Functions). குறியீடு சிக்கலானதாக மாறும்போது, அதை செயல்பாடுகளாக மாற்றி (refactor), அந்த செயல்பாடுகளைக் கட்டுமானக் கற்களாகப் பயன்படுத்தலாம். உண்மையில், நீங்கள் ஆரம்பத்திலிருந்தே செயல்பாடுகளைப் பயன்படுத்திக் கொண்டிருக்கிறீர்கள்; உதாரணமாக, Clojure-ல் (+ 1 2) என்று அழைக்கும்போது, ` என்பது ஒரு செயல்பாடு, மற்றும் `1` மற்றும் `2` என்பவை அது பெறும் மதிப்புகள். தொழில்நுட்ப ரீதியாக, ஒரு செயல்பாட்டால் பெறப்படும் மதிப்புகளை ஆர்கியுமெண்டுகள் (arguments) என்று அழைக்கிறோம். நீங்கள் `( 1 2) என்று இயக்கும்போது கணினியில் என்ன நடக்கிறது என்று உங்களுக்குத் தெரியாது; அது + என்ற செயல்பாட்டிற்குப் பின்னால் நேர்த்தியாக நுணுக்கப்படுத்தப்பட்டுள்ளது.

எனவே, இந்த அத்தியாயத்தில் நமது சொந்த செயல்பாடுகளை எவ்வாறு உருவாக்குவது என்று பார்ப்போம்.

9.1. செயல்பாடுகள் மூலம் வணக்கம் கூறுதல் (Saying Hello With Functions)

Clojure-ல் Hello World-ஐ அச்சிடுவதற்கான குறியீடு இதோ:

(println "Hello world!")

இப்போது, (say-hello) என்று அழைப்பதன் மூலம் இந்த செயல்பாட்டை ലഭ്യமாக்குவோம். அதற்கு, நாம் defn (define function என்பதன் சுருக்கம்) என்பதைப் பயன்படுத்துகிறோம்:

(defn )

முதலில், செயல்பாட்டின் பெயரான say-hello-வை ஒரு ஆர்கியுமெண்டாக வழங்குகிறோம்:

(defn say-hello)

say-hello இப்போது defn-க்கு வழங்கப்பட்ட பிறகு, நாம் ஆர்கியுமெண்டுகளைக் குறிப்பிட வேண்டும். 'Hello World!'-ஐ அச்சிட நமக்கு எந்த ஆர்கியுமெண்டுகளும் தேவையில்லை என்பதால், எந்த ஆர்கியுமெண்டுகளும் ஏற்கப்படாது என்பதைக் குறிக்க காலியான சதுர அடைப்புக்குறிகளை [] பயன்படுத்துகிறோம்.

(defn say-hello [])

இப்போது, செயல்பாட்டின் உடலை (function body) எழுதுவோம். இந்த வழக்கில், கீழே காட்டியபடி நாம் எளிமையாக 'Hello World!'-ஐ அச்சிடுகிறோம்:

(defn say-hello []
  (println "Hello world!"))

நாம் எப்போது (say-hello) என்று அழைத்தாலும், 'Hello World!' அச்சிடப்படும். say-hello-ன் இறுதிப் பதிப்பை கீழே உள்ள குறியீடு காட்டுகிறது:

;; function_say_hello.clj

(defn say-hello []
  (println "Hello world!"))

(say-hello)

வெளியீடு (Output)

Hello world!
nil

9.2. ஆர்கியுமெண்டுகளை வழங்குதல் (Passing Arguments)

நாம் செயல்பாடுகளுக்கு ஆர்கியுமெண்டுகளை வழங்க முடியும். உதாரணமாக, \$f(x) = x^2 + 2\$ என்ற செயல்பாட்டை எடுத்துக் கொள்ளுங்கள். இந்தச் செயல்பாடு \$x\$ என்ற ஆர்கியுமெண்ட்டை ஏற்கிறது; \$x\$-க்கு 7 என்ற மதிப்பை வழங்கினால், நமக்கு \$f(7) = 51\$ கிடைக்கும். அதேபோல், Clojure-ல் ஒரு செயல்பாடு ஒரு ஆர்கியுமெண்ட்டை ஏற்றுக்கொண்டு அதைக் கொண்டு ஒரு செயல்பாட்டைச் செய்ய முடியும்.

கீழே உள்ள வணக்கம் கூறும் உதாரணத்தைப் பார்த்து, அதைத் தட்டச்சு செய்து இயக்கவும்:

;; function_with_arguments.clj

(defn say-hello [name]
  (println "Hello" name "!"))

(say-hello "Karthik")

வெளியீடு (Output)

Hello Karthik !
nil

இது Hello Karthik ! என்று அச்சிடுகிறது. ஆனால் இது எப்படி செயல்படுகிறது? (defn say-hello …​) என்று எழுதுவதன் மூலம், say-hello என்ற பெயரில் ஒரு செயல்பாட்டை வரையறுக்கிறோம். கீழே காட்டியுள்ள [name] பகுதியை கவனியுங்கள்:

(defn say-hello [name]
  ;; செயல்பாட்டின் உடல் இங்கே வரும்
  )

name என்பது செயல்பாடு இயங்குவதற்கு வழங்கப்பட வேண்டிய ஒரு ஆர்கியுமெண்ட் ஆகும். இதை செயல்பாட்டின் உடலுக்குள் ஒரு மாறியாக (variable) பயன்படுத்தலாம். செயல்பாட்டின் கட்டமைப்பைக் கவனியுங்கள்: இது defn-ல் தொடங்கி, செயல்பாட்டின் பெயர் say-hello மற்றும் பின்னர் ஆர்கியுமெண்ட் பெயரைக் கொண்ட சதுர அடைப்புக்குறிகள் [name] என வருகிறது. இப்போது அதன் உடலை எழுதி செயல்பாட்டை நிறைவு செய்வோம்:

(defn say-hello [name]
  (println "Hello" name "!"))

செயல்பாட்டின் உடலிலும், நாம் name-க்கு வாழ்த்துகளை அச்சிடுகிறோம். name-ஐ println-க்கு இவ்வாறு வழங்குகிறோம்: (println "Hello" name "!").

"Karthik" என்ற ஆர்கியுமெண்ட்டுடன் say-hello-வை அழைப்பது: (say-hello "Karthik") என்பது Hello Karthik ! என்று அச்சிடும். உங்களுக்காக வாழ்த்துச் சொல்லுமாறு நிரலை மாற்றியமைக்கவும்.

9.2.1. வட்டத்தின் பரப்பளவைக் கண்டறிதல்

ஒரு வட்டத்தின் பரப்பளவு \$pi r^2\$ என கணக்கிடப்படுகிறது. வட்டத்தின் ஆரம் radius என்ற மாறியில் சேமிக்கப்பட்டிருந்தால், பரப்பளவைக் கண்டறியும் Clojure குறியீட்டை கீழே உள்ளபடி எழுதலாம்:

(* Math/PI (Math/pow radius 2))

ஒவ்வொரு முறையும் இந்தக் கணக்கீட்டை எழுதுவதற்குப் பதிலாக, circle-area என்ற செயல்பாட்டை அழைத்து பரப்பளவைக் கண்டறிவது சிறந்தது. கீழே உள்ள குறியீட்டில் அதை எவ்வாறு செயல்படுத்துகிறோம் என்று பாருங்கள்:

;; function_circle_area.clj

(defn circle-area [radius]
  (* Math/PI (Math/pow radius 2)))

(circle-area 7)

வெளியீடு (Output)

153.93804002589985

மேலே உள்ள குறியீட்டில், ஒவ்வொரு முறையும் Math/PI மற்றும் பவர் (power) கணக்கீடுகளைக் கையாளுவதற்குப் பதிலாக, நாம் (circle-area 7) என்று அழைத்தால் போதும், 7 யூனிட் ஆரம் கொண்ட வட்டத்தின் பரப்பளவு கிடைத்துவிடும். ஒரு சிறப்பாக வடிவமைக்கப்பட்ட செயல்பாடு சிக்கல்களை நுணுக்கப்படுத்தி, சிறந்த நிரல்களை எழுத அனுமதிக்கிறது.

9.3. ரீஃபாக்டரிங் (Refactoring)

உங்கள் நண்பர் ஒரு பில்லியன் டாலர் ஸ்டார்ட்அப் நிறுவனத்தைத் தொடங்குவதாகக் கற்பனை செய்து கொள்ளுங்கள், அதில் வாடிக்கையாளர்கள் வட்டத்தின் பரப்பளவைக் கணக்கிட உதவும் ஒரு செயலி (app) உள்ளது. ஒரு வெற்றிகரமான பிட்ச் மற்றும் முதலீட்டு சுற்றுக்குப் பிறகு, ஒவ்வொரு நாளும் ஆயிரக்கணக்கான மக்கள் வட்டத்தின் பரப்பளவைக் கணக்கிட விரும்புகிறார்கள் என்றும், அவரது செயலி நம்பமுடியாத அளவிற்கு பயனுள்ளதாக இருக்கும் என்றும் உங்கள் நண்பர் கூறினார். நீங்கள் ஒரு சிறந்த நிரலாளர் என்று அவருக்குத் தெரியும், எனவே நீங்கள் அவருக்காக circle-area செயல்பாட்டை எழுதினீர்கள். செயலி வெளியிடப்பட்டது, மனிதகுலம் காப்பாற்றப்பட்டது.

(* Math/PI (Math/pow radius 2)) மூலம் பரப்பளவைக் கணக்கிடுவதற்குப் பதிலாக, நீங்கள் எளிமையாக (* Math/PI radius radius) என்பதைப் பயன்படுத்தலாம் என்பதை உணர்கிறீர்கள். இரண்டாவது முறை மிகவும் எளிமையானது மற்றும் பராமரிக்க எளிதானது. फलस्वरूप, கீழே காட்டியபடி ஒரே ஒரு இடத்தில் குறியீட்டை மாற்றினால் போதுமானது:

;; function_circle_area_refactored.clj

(defn circle-area [radius]
  (* Math/PI radius radius))

(circle-area 7)

செயலியின் மற்ற அனைத்துக் குறியீடுகளும் இந்த மாற்றத்தைப் பற்றி அறியாமலே, மிகச்சரியாகத் தொடர்ந்து இயங்குகின்றன. நீங்கள் இந்தத் தர்க்கத்தை ஒரு செயல்பாடாக நுணுக்கப்படுத்தாமல், செயலியின் 50 வெவ்வேறு இடங்களில் (* Math/PI (Math/pow radius 2)) என்று எழுதியிருந்தால், இத்தகைய மாற்றத்தைச் செய்வதும் அதைச் சரிபார்ப்பதும் நம்பமுடியாத அளவிற்கு கடினமாக இருந்திருக்கும்.

எனவே, செயல்பாடுகள் சிறந்த குறியீடுகளுக்கு வழிவகுக்கின்றன மற்றும் பிழைகள் (bugs) ஏற்படுவதற்கான வாய்ப்பைக் குறைக்கின்றன.

9.4. பல ஆர்கியுமெண்டுகளைக் கொண்ட செயல்பாடுகள் (Functions with Multiple Arguments)

இந்தப் பகுதிக்கான வீடியோவை இங்கே காணலாம் https://yu7.in/clj-mul-arg

செயல்பாடுகள் ஒரு ஒற்றை ஆர்கியுமெண்ட்டுடன் மட்டும் மட்டுப்படுத்தப்படவில்லை. உதாரணமாக, \$a\$ மற்றும் \$b\$ நீளங்களைக் கொண்ட ஒரு செங்கோண முக்கோணத்தின் கர்ணத்தைக் (hypotenuse) கணக்கிட விரும்பினால், அதை \$f(a, b) = \sqrt{a^2 + b^2}\$ என்ற செயல்பாடாகக் குறிக்கலாம். இப்போது இதை Clojure-ல் குறியீடாக எழுதுவோம்:

;; hypotenuse.clj

(defn hypotenuse [a b]
  (Math/sqrt (+ (* a a) (* b b))))

(hypotenuse 3 4)

வெளியீடு (Output)

5.0

மேலே உள்ள குறியீட்டில், a மற்றும் b ஆகிய இரண்டு ஆர்கியுமெண்டுகளை ஏற்கும் hypotenuse என்ற செயல்பாட்டை வரையறுக்கிறோம். ஒரு செயல்பாடு எந்த ஆர்கியுமெண்ட்டையும் ஏற்கவில்லை என்றால், செயல்பாட்டின் பெயரைத் தொடர்ந்து காலியான சதுர அடைப்புக்குறிகள் [] வரும்; அது ஆர்கியுமெண்டுகளை ஏற்கும் பட்சத்தில், அவற்றின் பெயர்கள் அந்த அடைப்புக்குறிகளுக்குள் சேர்க்கப்படும், உதாரணமாக [a b]. செயல்பாட்டின் உடலிலும், கர்ணத்தைக் கணக்கிட்டு வழங்க (Math/sqrt (+ (* a a) (* b b)))-ஐப் பயன்படுத்துகிறோம்.

நாம் (hypotenuse 3 4) என்று அழைக்கும்போது, 5.0 திரும்பக் கிடைக்கிறது.

உங்களுக்கு ஒன்று அல்லது இரண்டு ஆர்கியுமெண்டுகளை ஏற்கும் செயல்பாடு தேவைப்பட்டால், Clojure-ல் ஃபங்ஷன் ஓவர்லோடிங் (function overloading) மூலம் இதைச் செய்ய முடியும். கீழே உள்ள குறியீட்டைப் பாருங்கள்:

;; function_multiple_arguments.clj

(defn multiple-args
  ([arg-1] (println "One argument passed:" arg-1))
  ([arg-1 arg-2] (println "Two arguments passed:" arg-1 arg-2)))

(multiple-args 1)
(multiple-args 1 2)

வெளியீடு (Output)

One argument passed: 1
Two arguments passed: 1 2

multiple-args என்ற செயல்பாட்டை இவ்வாறு வரையறுத்துள்ளோம்:

(defn multiple-args
  ;; செயல்பாட்டின் உடல் இங்கே வரும்
  )

இந்த செயல்பாடு ஒரு ஒற்றை ஆர்கியுமெண்ட்டை ஏற்பதற்கு, கீழே காட்டியபடி ஒரு வடிவத்தைச் சேர்க்கிறோம்:

(defn multiple-args
  ([arg-1] (println "One argument passed:" arg-1)))

இந்த வடிவம்:

([arg-1] (println "One argument passed:" arg-1))

arg-1 என்ற ஒரு ஆர்கியுமெண்ட்டைப் பெறுகிறது:

([arg-1] ...)

செயல்பாட்டின் உடலிலும், (println "One argument passed:" arg-1) மூலம் ஆர்கியுமெண்ட்டை அச்சிடுகிறோம்:

([arg-1] (println "One argument passed:" arg-1))

எனவே இது (multiple-args 1) போன்ற செயல்பாட்டு அழைப்பிற்குப் பதிலளிக்கும்.

இரண்டு ஆர்கியுமெண்டுகளை ஆதரிக்க, இரண்டு அளவுருக்களைக் கொண்ட மற்றொரு வடிவத்தைச் சேர்க்கிறோம்:

([arg-1 arg-2] (println "Two arguments passed:" arg-1 arg-2))

இதுவே ஒன்று அல்லது இரண்டு ஆர்கியுமெண்டுகளை ஏற்கக்கூடிய முழுமையான செயல்பாட்டு வரையறை ஆகும்:

(defn multiple-args
  ([arg-1] (println "One argument passed:" arg-1))
  ([arg-1 arg-2] (println "Two arguments passed:" arg-1 arg-2)))

நாம் (multiple-args 1 2) என்று அழைக்கும்போது, ([arg-1 arg-2] (println "Two arguments passed:" arg-1 arg-2)) இயக்கப்படுகிறது.

ஒரு பயிற்சியாக, (multiple-args) என்று அழைத்தால் 'No argument passed' என்று அச்சிடும் குறியீட்டை எழுத முயற்சி செய்யுங்கள். இது கடினமாக இருந்தால், குறியீடு உதாரணங்களில் உள்ள function_multiple_arguments_exercise.clj-ஐப் பார்க்கவும்.

9.5. வரம்பற்ற ஆர்கியுமெண்டுகளை ஏற்றுக்கொள்வது (Accepting Unlimited Arguments)

Clojure செயல்பாடுகள் வரம்பற்ற எண்ணிக்கையிலான ஆர்கியுமெண்டுகளை ஏற்க முடியும். உதாரணமாக, + செயல்பாடு எத்தனை ஆர்கியுமெண்டுகளை வேண்டுமானாலும் ஏற்றுக்கொண்டு அவற்றின் கூடுதலை வழங்க முடியும்:

(+ 1 2 3 5 7 -1)

வெளியீடு (Output)

17

ஒரு செயல்பாடு வரம்பற்ற எண்ணிக்கையிலான ஆர்கியுமெண்டுகளை ஏற்க வேண்டுமானால், ஆர்கியுமெண்ட் வெக்டரில் உள்ள கடைசி ஆர்கியுமெண்ட்டிற்கு முன்னால் & குறியீட்டைச் சேர்க்க வேண்டும். கீழே உள்ள குறியீட்டைப் பாருங்கள்:

;; function_unlimited_arguments.clj

(defn unlimited-arguments [& args]
  (println "Arguments:" args)
  (println "Type of args:" (type args)))

(unlimited-arguments 1)
(println)
(unlimited-arguments 1 17 true "Karthik" :coder)

வெளியீடு (Output)

Arguments: (1)
Type of args: clojure.lang.ArraySeq

Arguments: (1 17 true Karthik :coder)
Type of args: clojure.lang.ArraySeq

இந்த உதாரணத்தில், ஆர்கியுமெண்ட் வெக்டரில் உள்ள & args என்பது unlimited-arguments செயல்பாடு எத்தனை ஆர்கியுமெண்டுகளையும் ஏற்க அனுமதிக்கிறது.

உதாரணமாக, நாம் செயல்பாட்டை இவ்வாறு அழைத்தால்:

(unlimited-arguments 1)

நமக்கு Arguments: (1) என்ற வெளியீடு கிடைக்கிறது, இது இந்த கூற்றினால் உருவாக்கப்படுகிறது:

(println "Arguments:" args)

முந்தைய பகுதியில், நாம் ஆர்கியுமெண்டுகளை அச்சிட்டோம். அடுத்த பகுதியில்:

(println "Type of args:" (type args))

நாம் args-ன் வகையை அச்சிடுகிறோம், அது clojure.lang.ArraySeq (ஒரு வரிசை வகை) ஆகும். அதேபோல், நாம் அழைக்கும்போது:

(unlimited-arguments 1 17 true "Karthik" :coder)

அனைத்து ஆர்கியுமெண்டுகளும்— 1, 17, true, "Karthik", மற்றும் :coder—ஒரு வரிசையாக args-ல் தொகுக்கப்படுகின்றன, இது வெளியீட்டில் காணப்படுகிறது: Arguments: (1 17 true Karthik :coder).

ஒன்று அல்லது அதற்கு மேற்பட்ட ஆர்கியுமெண்டுகளை ஏற்கும் செயல்பாட்டை வரையறுக்க, அதை இவ்வாறு எழுதலாம்:

;; function_unlimited_arguments_2.clj

(defn unlimited-arguments [first-arg & args]
  (println "First argument:" first-arg)
  (println "Other arguments:" args))

(unlimited-arguments 1)
(println)
(unlimited-arguments 1 17 true "Karthik" :coder)

வெளியீடு (Output)

First argument: 1
Other arguments: nil

First argument: 1
Other arguments: (17 true Karthik :coder)

ஆர்கியுமெண்ட் வெக்டரான [first-arg & args]-ல், முதல் ஆர்கியுமெண்ட் first-arg-ஆல் கைப்பற்றப்படுகிறது, மற்றும் அதைத் தொடரும் மற்ற அனைத்து ஆர்கியுமெண்டுகளும் args-ல் தொகுக்கப்படுகின்றன.

பின்வரும் உதாரணத்தில், unlimited-arguments செயல்பாடு குறைந்தபட்சம் இரண்டு ஆர்கியுமெண்டுகளைக் கோருகிறது:

;; function_unlimited_arguments_3.clj

(defn unlimited-arguments [first-arg second-arg & args]
  (println "First argument:" first-arg)
  (println "Second argument:" second-arg)
  (println "Other arguments:" args))

(unlimited-arguments 1 2)
(println)
(unlimited-arguments 1 17 true "Karthik" :coder)

வெளியீடு (Output)

First argument: 1
Second argument: 2
Other arguments: nil

First argument: 1
Second argument: 17
Other arguments: (true Karthik :coder)

முதல் ஆர்கியுமெண்ட் first-arg-ஆலும், இரண்டாவது second-arg-ஆலும் கைப்பற்றப்படுகிறது, மற்றும் கூடுதல் ஆர்கியுமெண்டுகள் args-ல் ஒரு வரிசையாகத் தொகுக்கப்படுகின்றன.

9.6. மதிப்புகளைத் திருப்புதல் (Returning Values)

இந்தப் பகுதிக்கான வீடியோவை இங்கே காணலாம் https://yu7.in/clj-ret

ஒரு கணிதச் செயல்பாட்டை எடுத்துக் கொள்ளுங்கள், உதாரணமாக \$f(x) = x^2 + 2\$. நீங்கள் ஒரு மதிப்பை, உதாரணமாக \$x = 7\$ என்று வழங்கினால், செயல்பாடு 51-ஐத் திருப்பிக் கொடுக்கும். அதேபோல், ஒரு நிரலாக்க செயல்பாடு ஒரு மதிப்பைத் திருப்பிக் கொடுக்கும் என்று நீங்கள் எதிர்பார்க்கிறீர்கள்.

Clojure-ல், ஒரு செயல்பாட்டால் இயக்கப்படும் கடைசி வெளிப்பாட்டின் (last expression) முடிவே அதன் ரிட்டர்ன் மதிப்பாகும். உதாரணமாக, கீழே உள்ள குறியீட்டைப் பாருங்கள்:

;; function_returning_something.clj

(defn add [a b]
  (+ a b))

(println (add 2  3))

வெளியீடு (Output)

5
nil

மேலே உள்ள குறியீட்டில், (+ a b)-ன் முடிவு add செயல்பாட்டிலிருந்து திருப்பிக் கொடுக்கப்படுகிறது. (+ a b) என்பது add செயல்பாட்டின் கடைசி வெளிப்பாடு என்பதால், அதன் முடிவு திருப்பிக் கொடுக்கப்பட்டு, println-ஆல் கைப்பற்றப்பட்டு, பின்னர் அச்சிடப்படுகிறது. வெளியீட்டில் nil இருப்பதை நீங்கள் கவனித்திருக்கலாம். இதற்குக் காரணம் println, 5-ஐ அச்சிட்ட பிறகு nil-ஐத் திருப்பிக் கொடுக்கும், அது REPL-ஆல் அச்சிடப்படுகிறது.

Clojure-ல் உள்ள எந்தவொரு உள்ளமைக்கப்பட்ட கணித செயல்பாடும் இதற்கு ஒரு தெளிவான உதாரணம், உதாரணமாக +:

(+ 1 2 3 4 5)

இது 15-ஐத் திருப்பிக் கொடுக்கும். இதை நீங்கள் REPL-ல் முயற்சித்தால், வெளிப்பாடு ஒரு மதிப்பிற்கு மதிப்பீடு செய்யப்படுவதால் nil-க்கு பதிலாக 15 என்பதைக் காண்பீர்கள். Clojure-ல், nil என்பது மதிப்பின் இல்லாமையை அல்லது 'வெறுமையை’க் குறிக்கிறது.

ரிட்டர்ன் மதிப்பு எவ்வாறு தீர்மானிக்கப்படுகிறது என்பதைப் பார்க்க ஒரு செயல்பாட்டை எழுதுவோம். பின்வரும் உதாரணத்தை இயக்க முயற்சி செய்யுங்கள்:

;; what_it_returns.clj

(defn do-math [a b]
  (+ a b)
  (* a b))

(do-math 5 3)

வெளியீடு (Output)

15

do-math செயல்பாட்டில், முதல் வெளிப்பாடு (+ a b). (do-math 5 3) அழைக்கப்படும்போது, (+ a b) என்பது 8 என மதிப்பீடு செய்யப்படுகிறது, ஆனால் இந்த முடிவு புறக்கணிக்கப்படுகிறது. கடைசி வெளிப்பாடு (* a b), இது 15-ஐ வழங்குகிறது; இதுதான் do-math-ன் இறுதி ரிட்டர்ன் மதிப்பு மற்றும் வெளியீட்டில் தோன்றும் மதிப்பு.

இப்போது, * மற்றும் + ஆபரேட்டர்களை இடம் மாற்றி மற்றொரு செயல்பாட்டை வரையறுப்போம், அதை கீழே காணலாம்:

;; what_it_returns_2.clj

(defn do-math [a b]
  (* a b)
  (+ a b))

(do-math 5 3)

வெளியீடு (Output)

8

மேலே உள்ள குறியீட்டில், (+ a b) கடைசி வெளிப்பாடு என்பதால், அதன் முடிவு திருப்பிக் கொடுக்கப்படுகிறது, மற்றும் (* a b)-ன் முடிவு புறக்கணிக்கப்படுகிறது.

முக்கியமாக கவனிக்க வேண்டியது என்னவென்றால், ஒரு செயல்பாட்டின் கடைசி வெளிப்பாட்டின் முடிவே திருப்பிக் கொடுக்கப்படுகிறது.

9.7. மறுசெய்கை (Recursion)

இந்தப் பகுதிக்கான வீடியோவை இங்கே காணலாம் https://yu7.in/clj-recursion

ஒரு செயல்பாடு தன்னைத்தானே அழைத்துக் கொள்வது மறுசெய்கை (recursion) என்று அழைக்கப்படுகிறது. உதாரணமாக, கீழே உள்ள குறியீட்டைப் பாருங்கள்:

;; function_recursion.clj

(defn count-down [number]
  (println number)
  (if (pos? (dec number))
    (count-down (dec number))))

(count-down 5)

வெளியீடு (Output)

5
4
3
2
1
nil

இந்தச் செயல்பாடு இயக்கப்படும்போது, இது 5 முதல் 1 வரையிலான எண்களை அச்சிட்டு பின்னர் நின்றுவிடும். நாம் செயல்பாட்டை இவ்வாறு அழைத்தோம்: (count-down 5). செயல்பாட்டின் உடலை (function body) ஆராய்வோம்:

(println number)
(if (pos? (dec number))
  (count-down (dec number)))

முதலில், (println number) என்ற வெளிப்பாடு 5-ஐ அச்சிடுகிறது, பின்னர் இயக்கம் பின்வரும் வடிவத்திற்கு நகர்கிறது:

(if (pos? (dec number))
  (count-down (dec number)))

மேலே உள்ள குறியீட்டில், (dec number) என்பது 4 என மதிப்பீடு செய்யப்படுகிறது. 4 ஒரு நேர்மறை எண் என்பதால், (pos? 4) என்பது true ஆகும், மேலும் (count-down (dec number)) என்ற வெளிப்பாடு இயக்கப்பட்டு, (count-down 4) அழைக்கப்படுகிறது. அதாவது, count-down செயல்பாடு தன்னைத்தானே அழைக்கிறது.

இந்த செயல்முறை number பூஜ்ஜியமாகும் வரை தொடர்கிறது. அந்த நேரத்தில், (dec number) என்பது -1 ஆகிறது, இதனால் (pos? -1) என்பது false ஆகிறது. அதன் விளைவாக, (count-down (dec number)) இயக்கப்படாது, மேலும் மறுசெய்கை முடிவடைகிறது.

முந்தைய உதாரணத்தில் பார்த்தது போல, செயல்பாட்டின் பெயரைக் கொண்டு அழைப்பதன் மூலமோ அல்லது கீழே காட்டப்பட்டுள்ள recur என்ற சிறப்பு வடிவத்தைப் பயன்படுத்துவதன் மூலமோ நீங்கள் மறுசெய்கையைச் செய்யலாம்:

;; function_recur.clj

(defn count-down [number]
  (println number)
  (if (pos?  (dec number))
    (recur (dec number))))

(count-down 5)

வெளியீடு (Output)

5
4
3
2
1
nil

மேலே உள்ள உதாரணத்தில், செயல்பாடு தன்னைத்தானே அழைக்கும்போது, செயல்பாட்டின் பெயரான count-down-க்கு பதிலாக recur-ஐப் பயன்படுத்தினோம். recur-ஐப் பயன்படுத்துவது நினைவகத் திறன் (memory-efficient) மிக்கது, ஏனெனில் இது வால்-கால் உகப்பாக்கத்தை (tail-call optimization) செய்கிறது. recur வேலை செய்ய வேண்டுமானால், அது செயல்பாட்டில் இயக்கப்படும் கடைசி வெளிப்பாடாக இருக்க வேண்டும்.

பின்வரும் உதாரணத்தில், எண்களின் வரிசையின் கூடுதலைக் கணக்கிட மறுசெய்கையைப் பயன்படுத்துகிறோம். கீழே உள்ள குறியீட்டை இயக்கிப் பாருங்கள், அதன் செயல்பாட்டை நாம் விரைவில் ஆராய்வோம்.

;; function_sum_using_recursion.clj

(defn sum [numbers total]
  (if (empty? numbers)
    total
    (recur (rest numbers) (+ total (first numbers)))))

(println (sum [1 2 3 4 5] 0))

வெளியீடு (Output)

15
nil

முந்தைய உதாரணத்தில், நாம் (sum [1 2 3 4 5] 0) என்று அழைத்தோம், அது 15-ஐ வழங்கியது. ஆய்வை எளிதாக்க, ஒரு சிறிய உதாரணத்தைப் பயன்படுத்துவோம்:

(sum [1 2] 0)

எனவே, மேலே உள்ள குறியீடு இயக்கப்படும்போது என்ன நடக்கிறது? sum-ன் உடலைப் பார்ப்போம்:

(if (empty? numbers)
  total
  (recur (rest numbers) (+ total (first numbers))))

(empty? [1 2]) என்பது false என மதிப்பீடு செய்யப்படுகிறது, எனவே பின்வரும் வெளிப்பாடு இயக்கப்படுகிறது:

(recur (rest numbers) (+ total (first numbers)))

மதிப்புகளைப் பிரதியிட்டால், நமக்குக் கிடைப்பது:

(recur (rest [1 2]) (+ 0 (first [1 2])))

இதை இவ்வாறு சுருக்கலாம்:

(recur [2] (+ 0 1))

இது இவ்வாறு எளிமைப்படுத்தப்படுகிறது:

(recur [2] 1)

recur தற்போதைய செயல்பாட்டை அழைப்பதால், இதை இவ்வாறு குறிப்பிடலாம்:

(sum [2] 1)

இப்போது, sum செயல்பாட்டிற்கு numbers ஆக [2] மற்றும் total ஆக 1 வழங்கப்படுகிறது. (empty? [2]) மீண்டும் false என்பதால், நாம் பின்வரும் வெளிப்பாட்டை அடைகிறோம்:

(recur (rest numbers) (+ total (first numbers)))

மதிப்புகளைப் பிரதியிட்டால், நமக்குக் கிடைப்பது:

(recur (rest [2]) (+ 1 (first [2])))

வெளிப்பாட்டைச் சுருக்கினால், நமக்குக் கிடைப்பது:

(recur [] (+ 1 2))

recur-க்கு பதிலாக sum-ஐப் பயன்படுத்தியும், (+ 1 2) என்பதை 3 என எளிமைப்படுத்தியும், நமக்குக் கிடைப்பது:

(sum [] 3)

இப்போது numbers என்பது [] மற்றும் total என்பது 3. இவற்றை செயல்பாட்டில் இடுவோம்:

(if (empty? numbers)
  total
  (recur (rest numbers) (+ total (first numbers))))

இங்கு, (empty? numbers) என்பது true ஆகும், எனவே total திரும்பக் கிடைக்கிறது. இவ்வாறு, 3 திரும்பக் கிடைக்கிறது, இது வெக்டர் [1 2]-ன் கூடுதலாகும்.

ஆரம்பக் கூடுதல் (initial total) தேவையின்றி, ஒரு வரிசையை மட்டும் ஏற்றுக்கொண்டு அதன் கூடுதலைக் கணக்கிடும் செயல்பாடு வேண்டுமென்றால் என்ன செய்வது? கீழே உள்ள உதாரணத்தைப் பாருங்கள்:

;; function_collection_sum.clj

(defn sum [numbers total]
  (if (empty? numbers)
    total
    (recur (rest numbers) (+ total (first numbers)))))

(defn collection-sum [collection]
  (sum collection 0))

(println (collection-sum [1 2 3 4 5]))

வெளியீடு (Output)

15
nil

இந்த உதாரணத்தில், collection-sum செயல்பாடு ஒரு தொகுப்பை (collection) ஏற்கிறது. இது அந்தத் தொகுப்பையும், ஆரம்பக் கூடுதலான 0-வையும் ஏற்கனவே வரையறுக்கப்பட்ட sum செயல்பாட்டிற்கு வழங்குவதன் மூலம் இந்த செயல்முறையை நுணுக்கப்படுத்துகிறது, இது கூடுதலைக் கணக்கிட மறுசெய்கையைப் பயன்படுத்துகிறது.

9.8. மல்டிமெத்தட்ஸ் (Multimethods)

இந்தப் பகுதிக்கான வீடியோவை இங்கே காணலாம் https://yu7.in/clj-mm மற்றும் இங்கே https://yu7.in/clj-mmm

ஒரு செயல்பாட்டிற்கு நீங்கள் ஆர்கியுமெண்டுகளை வழங்குகிறீர்கள் என்றும், வழங்கப்பட்ட மதிப்புகளின் அடிப்படையில், எந்த குறிப்பிட்ட மெத்தட் (method) அழைக்கப்பட வேண்டும் என்பதை Clojure தீர்மானிக்கிறது என்றும் வைத்துக்கொள்வோம். இந்த நுட்பம் மல்டிமெத்தட்ஸ் (multimethods) என்று அழைக்கப்படுகிறது.

கீழே உள்ள குறியீட்டை ஆராய்ந்து, பின்னர் அதைத் தட்டச்சு செய்து இயக்கவும்:

;; multimethod_factorial.clj

(defmulti factorial identity)

(defmethod factorial 0 [_]  1)

(defmethod factorial :default [num]
  (* num (factorial (dec num))))

(factorial 0) ; => 1
(factorial 1) ; => 1
(factorial 3) ; => 6
(factorial 7) ; => 5040

(defmulti factorial identity) என்ற வரியில், factorial என்ற மல்டிமெத்தடை நாம் வரையறுக்கிறோம். டிஸ்பாட்ச் மதிப்பு (execution path-ஐ தீர்மானிப்பது) identity செயல்பாட்டால் வரையறுக்கப்படுகிறது. இப்போது, ஐடென்டிட்டி 0 ஆக இருக்கும்போது செயல்பாடு எப்படி இருக்க வேண்டும் என்பதை வரையறுப்போம்:

(defmethod factorial 0)

அடுத்து, ஆர்கியுமெண்டுகளைக் குறிப்பிடுகிறோம்:

(defmethod factorial 0 [_])

நாம் ஆர்கியுமெண்ட்டைப் பயன்படுத்தாததால், அண்டர்ஸ்கோர் _ பயன்படுத்தும் மரபைப் பின்பற்றுகிறோம். பின்னர், செயல்பாடு 1-ஐ வழங்க வேண்டும் என்று குறிப்பிடுகிறோம்:

(defmethod factorial 0 [_] 1)

மற்ற அனைத்து டிஸ்பாட்ச் மதிப்புகளுக்கும், நாம் :default என்ற கீவேர்டைப் பயன்படுத்துகிறோம்:

(defmethod factorial :default)

ஆர்கியுமெண்ட்டை num என வரையறுக்கிறோம்:

(defmethod factorial :default [num])

கீழே காட்டப்பட்டுள்ளபடி \$n * (n - 1)!\$-ஐ வழங்குகிறோம்:

(defmethod factorial :default [num]
  (* num (factorial (dec num))))

இது எந்த எண்ணின் ஃபேக்டோரியலையும் (factorial) கணக்கிடுவதற்குத் தெளிவான மற்றும் நேர்த்தியான குறியீட்டை வழங்குகிறது.

இப்போது கீழே உள்ள குறியீட்டைப் பாருங்கள்:

;; without_multimethods.clj

(defn print-welcome-message [person]
  (cond
    (string? person) (println "Welcome" person)
    (vector? person) (println  "Welcome" (first person) "from" (last person))
    (map? person)    (println "Welcome" (person "name") "from" (person "from"))))

(print-welcome-message "Karthik from Chennai")
(print-welcome-message ["Kalam" "Ramanthapuram"])
(print-welcome-message {"name" "Bharathiyaar" "from" "Yettaiyapuram"})

வெளியீடு (Output)

Welcome Karthik from Chennai
Welcome Kalam from Ramanthapuram
Welcome Bharathiyaar from Yettaiyapuram

print-welcome-message செயல்பாடு person-ஐ ஒரு ஆர்கியுமெண்டாக ஏற்கிறது. person ஒரு சரம் (string), வெக்டர் அல்லது மேப் என்பதில் பொறுத்து, நிரல் தரவை எடுத்து அச்சிடுகிறது, அது பின்வரும் பிளாக்கில் காட்டப்பட்டுள்ளது:

 (cond
    (string? person) (println "Welcome" person)
    (vector? person) (println  "Welcome" (first person) "from" (last person))
    (map? person)    (println "Welcome" (person "name") "from" (person "from")))

அதே செயல்பாட்டை மல்டிமெத்தட்ஸைப் பயன்படுத்தியும் செயல்படுத்த முடியும், அது கீழே உள்ள குறியீட்டில் காட்டப்பட்டுள்ளது. அதைத் தட்டச்சு செய்து இயக்கவும், நான் அதன் செயல்முறையை விளக்குகிறேன்:

;; with_multimethods.clj

(defn welcome-person [person]
  (cond
    (string? person) :welcome-person-string
    (vector? person) :welcome-person-vector
    (map? person)    :welcome-person-map))

(defmulti print-welcome-message welcome-person)

(defmethod print-welcome-message :welcome-person-string [person]
  (println "Welcome" person))

(defmethod print-welcome-message :welcome-person-vector [person]
  (println  "Welcome" (first person) "from" (last person)))

(defmethod print-welcome-message :welcome-person-map [person]
  (println "Welcome" (person "name") "from" (person "from")))

(print-welcome-message "Karthik from Chennai")
(print-welcome-message ["Kalam" "Ramanthapuram"])
(print-welcome-message {"name" "Bharathiyaar" "from" "Yettaiyapuram"})

வெளியீடு (Output)

Welcome Karthik from Chennai
Welcome Kalam from Ramanthapuram
Welcome Bharathiyaar from Yettaiyapuram

முதலில் உங்களுக்கு ஒரு மல்டிமெத்தட் வரையறை உள்ளது:

(defmulti print-welcome-message welcome-person)

இந்தக் கூற்று print-welcome-message என்ற மல்டிமெத்தடை வரையறுக்கிறது மற்றும் எந்த மெத்தடை இயக்க வேண்டும் என்பதை welcome-person செயல்பாடு தீர்மானிக்கும் என்று குறிப்பிடுகிறது.

இப்போது welcome-person-ல் என்ன உள்ளது என்று பார்ப்போம்:

(defn welcome-person [person]
  (cond
    (string? person) :welcome-person-string
    (vector? person) :welcome-person-vector
    (map? person)    :welcome-person-map))

welcome-person செயல்பாடு person-ஐ ஒரு ஆர்கியுமெண்டாக ஏற்றுக்கொண்டு, பொருத்தமான இயங்குப்பாதையை (execution path) அடையாளம் காணும் ஒரு கீவேர்டை வழங்குகிறது.

இப்போது மெத்தட்களின் வரையறையைப் பார்ப்போம்:

(defmethod print-welcome-message :welcome-person-string [person]
  (println "Welcome" person))

(defmethod print-welcome-message :welcome-person-vector [person]
  (println  "Welcome" (first person) "from" (last person)))

(defmethod print-welcome-message :welcome-person-map [person]
  (println "Welcome" (person "name") "from" (person "from")))

முதலில் உள்ளதைப் பார்ப்போம்:

(defmethod print-welcome-message :welcome-person-string [person]
  (println "Welcome" person))

welcome-person-ல், person ஒரு சரமாக இருந்தால், செயல்பாடு :welcome-person-string-ஐ வழங்கும். அதற்குரிய மெத்தட் மேலே defmethod-உடன் தொடங்க வரையறுக்கப்பட்டுள்ளது:

(defmethod)

அதற்குப் பிறகு மல்டிமெத்தட் பெயர் வரும்:

(defmethod print-welcome-message)

அடுத்து, welcome-person-ஆல் தீர்மானிக்கப்பட்ட கீவேர்டை வழங்குகிறோம். இந்த வழக்கில், person ஒரு சரமாக இருந்தால், இந்தக் குறிப்பிட்ட குறியீடு இயக்கப்படும்:

(defmethod print-welcome-message :welcome-person-string)

இதற்குப் பிறகு person ஆர்கியுமெண்ட் வரும்:

(defmethod print-welcome-message :welcome-person-string [person])

இறுதியாக, செயல்பாட்டின் உடலை, (println "Welcome" person) வழங்குகிறோம்:

(defmethod print-welcome-message :welcome-person-string [person]
  (println "Welcome" person))

தரவு வகையைப் பொறுத்து, welcome-person வெவ்வேறு கீவேர்டுகளை வழங்குகிறது, இது அதற்குரிய defmethod-ஐ இயக்கி வைக்கும். இது வெவ்வேறு இயங்குப்பாதிகளைத் தனித்தனி மெத்தட்களாகப் பிரிக்க அனுமதிக்கிறது, இதன் மூலம் குறியீடு எளிமையாகவும் பராமரிக்கக்கூடியதாகவும் இருக்கும்.

9.9. முன் மற்றும் பின் நிபந்தனை சரிபார்ப்பு (Pre and Post Condition Checking)

இந்தப் பகுதிக்கான வீடியோவை இங்கே காணலாம் https://yu7.in/clj-prepo

பெரும்பாலான செயல்பாடுகள் இயங்குவதற்குத் தரவு தேவைப்படுகிறது. தரவு சரியான வடிவத்தில் வழங்கப்படவில்லை என்றால், செயல்பாடு தோல்வியடையக்கூடும். ஒரு செயல்பாட்டிற்கு வழங்கப்படும் ஆர்கியுமெண்டுகளைச் சரிபார்ப்பது ஒரு சிறந்த நடைமுறையாகும். இதற்காக Clojure :pre ஹூக்-ஐ (hook) வழங்குகிறது. ஒரு உதாரணத்தின் மூலம் இது எவ்வாறு செயல்படுகிறது என்று பார்ப்போம். கீழே உள்ள குறியீட்டைத் தட்டச்சு செய்யவும்:

;; function_pre.clj

(defn sum [a b]
  {:pre [(number? a) (number? b)]}
  (+ a b))

(println (sum 4 5))
;; (println (sum "4" 5)) ;; Throws an error

(println (sum 4 5)) என்பதை இயக்குவது எதிர்பார்த்தபடி செயல்படுகிறது, ஆனால் (println (sum "4" 5)) ஒரு பிழையை (error) ஏற்படுத்தும். இது sum செயல்பாட்டில் உள்ள பின்வரும் குறியீட்டுப் பகுதியால் ஏற்படுகிறது:

{:pre [(number? a) (number? b)]}

இது :pre கீயைக் கொண்ட ஒரு மேப் (map) ஆகும். நாம் பல நிபந்தனைகளைச் சரிபார்க்க விரும்பலாம் என்பதால், அவற்றை ஒரு வெக்டராக வழங்குகிறோம். இந்த வழக்கில், முதல் ஆர்கியுமெண்ட் a ஒரு எண்ணா என்பதை (number? a) மூலமாகவும், இரண்டாவது ஆர்கியுமெண்ட் b ஒரு எண்ணா என்பதை (number? b) மூலமாகவும் சரிபார்க்கிறோம். வெக்டரில் உள்ள அனைத்து நிபந்தனைகளும் உண்மையாக (true) இருந்தால் மட்டுமே செயல்பாட்டின் உடல் (function body) இயக்கப்படும்.

(sum 4 5) என்பதில், a மற்றும் b ஆகிய இரண்டும் எண்கள், எனவே sum செயல்பாடு இயங்குகிறது. (sum "4" 5) என்பதில், "4" ஒரு சரமாகும் (string), இதனால் (number? a) என்பது பொய்யாக (false) மாறி, ஒரு பிழையை உருவாக்குகிறது.

திரும்பப் பெறப்படும் மதிப்பு ஒரு குறிப்பிட்ட வடிவம் அல்லது கட்டமைப்பைக் கொண்டுள்ளதா என்பதை நீங்கள் சரிபார்க்க வேண்டுமானால், கீழே காட்டியுள்ளபடி :post ஹூக்-ஐப் பயன்படுத்தலாம்:

;; function_post.clj

(defn sum [a b]
  {:post [(number? %)]}
  "45")

(defn sum-without-post [a b]
  "45")

(println (sum-without-post 4 5))
(println (sum 4 5)) ;; ; Assert failed: (number? %)

மேலே உள்ள குறியீட்டை இயக்கவும். இந்த உதாரணத்தில், sum மற்றும் sum-without-post ஆகிய இரண்டும் "45" என்ற சரத்தைத் திருப்பிக் கொடுக்கின்றன. (sum-without-post 4 5) என்பதை இயக்குவது எந்தப் பிரச்சனையும் இன்றி இயங்குகிறது. இருப்பினும், இரண்டு எண்களின் கூடுதல் ஒரு எண்ணாக இருக்க வேண்டும் என்பதால், (sum 4 5) ஒரு சரத்தைத் திருப்பிக் கொடுப்பதால் பிழையை ஏற்படுத்துகிறது, இது :post ஹூக்-ஐ மீறுவதாகும்:

{:post [(number? %)]}

:post ஹூக், திருப்பிக் கொடுக்கப்படும் மதிப்பு (% குறியீட்டால் குறிக்கப்படுகிறது) ஒரு எண்ணாக இருக்க வேண்டும் என்று எதிர்பார்க்கிறது.

இப்போது pre மற்றும் post ஹூக்குகள் எவ்வாறு செயல்படுகின்றன என்று பார்ப்போம்:

;; function_pre_post.clj

(defn sum [a b]
  {:pre [(number? a) (number? b)]
   :post [(number? %)]}
  (+ a b))

(println (sum 4 5))

மேலே உள்ள குறியீட்டைப் பயன்படுத்தி (sum 4 "5") என்று அழைத்தால் என்ன நடக்கும் என்று நீங்கள் நினைக்கிறீர்கள்?

9.10. டாக்ஸ்ட்ரிங்குகள் (Docstrings)

இந்தப் பகுதிக்கான வீடியோவை இங்கே காணலாம் https://yu7.in/clj-docstr

நிரலாக்கத்தில் ஆவணப்படுத்துதல் (Documentation) மிக முக்கியமானது. செயல்பாடுகளை ஆவணப்படுத்துவதற்கு Clojure ஒரு உள்ளமைக்கப்பட்ட வழியை வழங்குகிறது, இவை இந்த மொழியில் முதன்மையான அங்கங்களாகும் (first-class citizens). கீழே உள்ள குறியீட்டைப் பாருங்கள்:

;; docstring.clj

(defn sum
  "Adds two numbers passed as arguments.

   The arguments should be numbers.

   **Usage**

   ```clojure
   (sum 4 5) ;; returns 9
   ```
  "
  [a b]
  {:pre [(number? a) (number? b)]}
  (+ a b))

(println (sum 4 5))

(defn sum என்பதற்கு immediately பிறகு, நாம் ஒரு டாக்ஸ்ட்ரிங்கை வழங்குகிறோம்:

"Adds two numbers passed as arguments.

  The arguments should be numbers.

  **Usage**

  ```clojure
  (sum 4 5) ;; returns 9
  ```
"

இந்தச் சரம் செயல்பாட்டிற்கான ஆவணமாகப் பயன்படுகிறது. Markdown வடிவமைப்பைப் பின்பற்றும் Usage பயன்பாட்டைக் கவனியுங்கள். டாக்ஸ்ட்ரிங்கிற்குள் ஒரு குறியீட்டுத் தொகுப்பையும் (code block) நான் சேர்த்துள்ளேன்:

```clojure
(sum 4 5) ;; returns 9
```
இது ஆவணப் பார்வையாளருக்கு `(sum 4 5)

returns 9` என்பதை Clojure குறியீடாகக் காட்ட அறிவுறுத்துகிறது.

கீழே காட்டப்பட்டுள்ளபடி, doc செயல்பாட்டைப் பயன்படுத்தி sum-ன் ஆவணத்தை அணுகலாம்:

clj꞉user꞉> (doc sum)
-------------------------
user/sum
([a b])
  Adds two numbers passed as arguments.

   The arguments should be numbers.

   **Usage**

   ```clojure
   (sum 4 5) ;; returns 9
   ```

nil

மாற்றாக, VSCodium-ல், இங்கே காட்டப்பட்டுள்ளபடி செயல்பாட்டின் பெயரின் மேல் மவுஸை வைப்பதன் மூலம் ஆவணத்தை ஒரு நேர்த்தியான பாப்-அப்பில் (popup) காணலாம்:

docstring

9.11. பெயரற்ற செயல்பாடுகள் (Anonymous Functions)

இந்தப் பகுதிக்கான வீடியோவை இங்கே காணலாம் https://yu7.in/clj-frf

செயல்பாடுகளுக்குப் பெயர் சூட்டாமலேயே அவற்றை வரையறுக்க முடியும்; இவை பெயரற்ற செயல்பாடுகள் (anonymous functions) என்று அழைக்கப்படுகின்றன. கீழே உள்ள குறியீட்டை முயற்சித்துப் பாருங்கள்:

;; anonymous_function.clj

(def print-something
  (fn [something]
    (println something)))

(print-something "something is better than nothing")

வெளியீடு (Output)

something is better than nothing

இந்தக் குறியீட்டுப் பகுதியை (snippet) பாருங்கள்:

(fn [something]
  (println something))

இந்த வெளிப்பாடு something என்ற ஒற்றை ஆர்கியுமெண்ட்டை ஏற்கும் ஒரு செயல்பாட்டை வழங்குகிறது. இந்த செயல்பாடு ஒரு மதிப்பைக் கொண்டு அழைக்கப்படும்போது, அது அந்த மதிப்பை அச்சிடுகிறது. மேலே உள்ள குறியீட்டில் வழங்கப்பட்ட செயல்பாட்டிற்குப் பெயர் இல்லை என்பதைக் கவனியுங்கள்; இது ஒரு செயல்பாட்டு அமைப்பாக (functional entity) உள்ளது, ஆனால் அதற்கு அடையாளக் குறியீடு (symbolic identifier) இல்லை.

ஒரு பெயரற்ற செயல்பாட்டை ஒரு குறியீட்டுடன் (symbol) இணைப்பதன் மூலம் அதற்கு ஒரு பெயரை வழங்க முடியும், அது கீழே காட்டப்பட்டுள்ளது:

(def print-something
  (fn [something]
    (println something)))

மேலே உள்ள குறியீட்டில், பெயரற்ற செயல்பாடு print-something என்ற குறியீட்டுடன் இணைக்கப்பட்டுள்ளது. இப்போது நீங்கள் அந்தப் பெயரைப் பயன்படுத்தி அதை அழைக்கலாம்: (print-something "something is better than nothing").

உண்மையில், defn என்பது fn மூலம் ஒரு செயல்பாட்டை வரையறுப்பதையும், def மூலம் அதற்கு ஒரு பெயரை வழங்குவதையும் இணைக்கும் ஒரு குறுக்குவழியாகும்.

குறிப்பாக, இதை இவ்வாறு எழுதுவது:

(def print-something
  (fn [something]
    (println something)))

இதை இவ்வாறு எழுதுவதற்குச் சமமானது:

(defn print-something [something]
    (println something))

மேலும் இது ஒரே மாதிரியாகவே செயல்படும்.

9.12. செயல்பாடுகளைத் திருப்பிக் கொடுக்கும் செயல்பாடுகள் (Functions Returning Functions)

இந்தப் பகுதிக்கான வீடியோவை இங்கே காணலாம் https://yu7.in/clj-frf

பெயரற்ற செயல்பாடுகளின் பயன் என்ன என்று நீங்கள் யோசிக்கலாம்? கீழே உள்ள உதாரணத்தைப் பார்த்து அதை இயக்கிப் பாருங்கள்:

;; function_returning_function.clj

(defn multiplier [multiply-with]
  (fn [number]
    (* number multiply-with)))

(def double-it
  (multiplier 2))

(def triple-it
  (multiplier 3))

(double-it 21)

(triple-it 14)

(double-it 21) இயக்கப்பட்டால், அது 42-ஐத் திருப்பிக் கொடுக்கும். இப்போது, double-it-ன் வரையறையைப் பார்ப்போம்:

(def double-it
  (multiplier 2))

மேலே உள்ள குறியீட்டில், double-it-க்கு (multiplier 2)-ன் வெளியீடு ஒதுக்கப்பட்டுள்ளது. double-it ஒரு செயல்பாடாகச் செயல்படுவதால், (multiplier 2) ஒரு செயல்பாட்டைத் திருப்பிக் கொடுக்க வேண்டும். multiplier-ன் மூலக் குறியீட்டை ஆராய்வோம்:

(defn multiplier [multiply-with]
  (fn [number]
    (* number multiply-with)))

இப்போது multiplier எதைத் திருப்பிக் கொடுக்கிறது என்று பாருங்கள்:

(fn [number]
  (* number multiply-with))

மேலே காட்டியபடி, அது ஒரு செயல்பாட்டைத் திருப்பிக் கொடுக்கிறது. multiply-with-க்கு பதிலாக 2-ஐப் பயன்படுத்தினால், நமக்குக் கிடைப்பது இதுதான்:

(fn [number]
  (* number 2))

கிடைக்கும் குறியீடு ஒரு எண்ணை (number) ஏற்றுக்கொண்டு அதை 2-ஆல் பெருக்கி அதன் விடையைத் திருப்பிக் கொடுக்கும் ஒரு செயல்பாடாகும். இப்போது, இதை double-it-ன் வரையறையில் பிரதியிடுவோம்:

(def double-it
  (multiplier 2))

நமக்குக் கிடைப்பது இதுதான்:

(def double-it
  (fn [number]
    (* number 2)))

சுருக்கமாகச் சொன்னால், இதை இவ்வாறு எழுதியிருக்கலாம்:

(defn double-it [number]
    (* number 2))

multiplier செயல்பாட்டில், நாம் அடிப்படையில் புதிய செயல்பாடுகளை உருவாக்கி அவற்றைத் திருப்பிக் கொடுக்கிறோம், பின்னர் அவற்றுக்குப் பெயர் சூட்டுகிறோம். இது உயர்மட்ட செயல்பாடுகள் (higher-order functions) என்று அழைக்கப்படும் ஒரு சக்திவாய்ந்த கருத்தாகும்.

10. Calva மூலம் டிபக்கிங் செய்தல் (Debugging With Calva)

இந்தப் பகுதிக்கு நீங்கள் இந்தக் குறியீட்டைப் பயன்படுத்தலாம் https://gitlab.com/clojure-book/code/-/raw/master/executing_form.clj

பெரிய குறியீட்டுத் தொகுப்புகள் (codebases) பெரும்பாலும் பல சிறிய குறியீட்டுத் துண்டுகளை இணைப்பதன் மூலம் உருவாக்கப்படுகின்றன. இந்தத் துண்டுகள் எதிர்பார்த்தபடி செயல்படுவதை உறுதி செய்ய, அவற்றை படிப்படியாக இயக்கி மதிப்பீடு செய்ய ஒரு வழி தேவை.

Calva என்பது VS Code-க்கான ஒரு Clojure பிளகின் (plugin) ஆகும், இது குறியீடுகளை இயக்கவும் வெளிப்பாடுகளை (expressions) மதிப்பீடு செய்யவும் அனுமதிக்கிறது. இது சக்திவாய்ந்த டிபக்கிங் கருவிகளையும் வழங்குகிறது. இந்தப் பகுதியில், Clojure குறியீட்டை மதிப்பீடு செய்யவும் மற்றும் டிபக் செய்யவும் Calva-வை எவ்வாறு பயன்படுத்துவது என்று கற்றுக்கொள்வோம்.

10.1. REPL உடன் இணைத்தல் (Connecting to a REPL)

VS Code-ல், உங்கள் டெர்மினலைத் தொடங்கி lein repl என்று தட்டச்சு செய்யவும். உங்களுக்கு இது போன்ற ஒன்று தெரிய வேண்டும்:

$ lein repl
nREPL server started on port 62571 on host 127.0.0.1 - nrepl://127.0.0.1:62571
REPL-y 0.5.1, nREPL 1.0.0
Clojure 1.11.1
OpenJDK 64-Bit Server VM 25.0.2+10-LTS
    Docs: (doc function-name-here)
          (find-doc "part-of-name-here")
  Source: (source function-name-here)
 Javadoc: (javadoc java-object-or-class-here)
    Exit: Control+D or (exit) or (quit)
 Results: Stored in vars *1, *2, *3, an exception in *e

user=>

nrepl://127.0.0.1:62571-ஐக் கவனியுங்கள், இதில் 127.0.0.1 என்பது ஹோஸ்ட் (host) மற்றும் 62571 என்பது போர்ட் (port) ஆகும். REPL உடன் இணைக்க இந்தத் தகவலைப் பயன்படுத்துவோம்.

MacOS-ல் command+shift+p அல்லது Linux/Windows-ல் ctrl+shift+p அழுத்தி கமாண்ட் பேலெட்டைத் (command palette) திறக்கவும் மற்றும் Calva: Connect to Running REPL Server, not in Project என்று தட்டச்சு செய்யவும். உங்களுக்கு இது போன்ற ஒன்று தெரிய வேண்டும்:

connecting to repl

அந்த விருப்பத்தைத் தேர்ந்தெடுத்த பிறகு, உங்களுக்கு இது போன்ற ஒன்று தெரிய வேண்டும்:

selecting leiningen

Leiningen-ஐத் தேர்ந்தெடுக்கவும். உங்களுக்கு இது போன்ற ஒன்று தெரிய வேண்டும்:

entering the port

localhost:-க்கு பிறகு, நீங்கள் குறித்து வைத்த போர்ட் எண்ணை உள்ளிடவும். இது இங்கே காட்டப்பட்டுள்ளதிலிருந்து வேறுபடலாம்; இந்த உதாரணத்தில், அது 62571 ஆகும்.

இப்போது, நீங்கள் REPL உடன் இணைக்கப்பட்டிருப்பீர்கள்.

10.2. டாப் லெவல் ஃபார்மை இயக்குதல் (Executing Top Level Form)

executing_form.clj என்ற எளிய கோப்பை உருவாக்கி, அதில் பின்வரும் குறியீட்டைச் சேர்க்கவும்:

(+ 7
  (* 3 4))

இப்போது (+ 7 (* 3 4))-க்குள் எங்கு வேண்டுமானாலும் கர்சரை வைத்து, GNU/Linux-ல் alt+enter அல்லது macOS-ல் option+enter அழுத்தவும். உங்களுக்கு இது போன்ற ஒன்று தெரிய வேண்டும்:

executing top level form

வலதுபுறம், REPL-ல், முடிவு 19 என்று இருக்கும். குறியீட்டை மதிப்பீடு செய்து அதன் முடிவுகளை உடனடியாக REPL-ல் காண்பது மிகவும் வசதியானது.

10.3. தற்போதைய ஃபார்மை இயக்குதல் (Executing Current Form)

executing_form.clj-ல் நம்மிடம் உள்ள குறியீடு இதுதான்:

(+ 7
  (* 3 4))

இங்கே இரண்டு ஃபார்ம்கள் உள்ளன: டாப்-லெவல் ஃபார்ம் (+ 7 (* 3 4)) மற்றும் உட்புற ஃபார்ம் (inner form) (* 3 4).

(* 3 4)-ன் தொடக்க அடைப்புக்குறிக்கு முன்னால் எங்கு வேண்டுமானாலும் கர்சரை வைத்து, GNU/Linux மற்றும் macOS-ல் ctrl+enter அழுத்தவும். உங்களுக்கு இது போன்ற ஒன்று தெரிய வேண்டும்:

executing current form

முடிவு 12 என்று இருப்பதை கவனியுங்கள். ஏனெனில் கர்சர் (* 3 4) என்ற ஃபார்முக்கு சற்று முன்னால் இருந்தது, மேலும் ctrl+enter தற்போதைய ஃபார்மை இயக்குகிறது.

இப்போது டாப்-லெவல் ஃபார்மை இயக்குவோம். (+ 7 (* 3 4))-ன் வெளிப்புற அடைப்புக்குறிக்கு சற்று முன்னால் கர்சரை வைத்து, GNU/Linux அல்லது macOS-ல் ctrl+enter அழுத்தவும். உங்களுக்கு இது போன்ற ஒன்று தெரிய வேண்டும்:

executing top level form as current form

முடிவு 19 என்று இருப்பதை கவனியுங்கள். ஏனெனில் கர்சர் (+ 7 (* 3 4)) என்ற ஃபார்முக்கு வெளியே இருந்தது, மேலும் ctrl+enter தற்போதைய ஃபார்மை இயக்குகிறது, இந்த வழக்கில் அது முழு டாப்-லெவல் ஃபார்ம் ஆகும்.

இந்தப் பகுதியின் முக்கிய அம்சம் என்னவென்றால், ctrl+enter தற்போதைய ஃபார்மை இயக்குகிறது.

11. ஸ்பெக் (Spec)

இந்தப் பகுதிக்கு, நீங்கள் https://gitlab.com/clojure-book/code/-/raw/master/spec.clj-ல் உள்ள குறியீட்டைப் பயன்படுத்தலாம். உங்கள் REPL டெர்மினலில் இயங்கினாலும் அல்லது உங்கள் IDE-உடன் ஒருங்கிணைக்கப்பட்டிருந்தாலும், வரிகளை ஒவ்வொன்றாக நகலெடுத்து (copy) பேஸ்ட் (paste) செய்யவும்.
இந்தப் பகுதிக்கான வீடியோவை இங்கே காணலாம் https://yu7.in/clj-spec

Clojure நிரல்கள் தரவைக் கையாளும் செயல்பாடுகளைக் கொண்டுள்ளன. தரவு என்பது சரங்கள் (Strings), எண்கள் (Numbers), மேப்கள் (Maps), வெக்டர்கள் (Vectors) மற்றும் செட்கள் (Sets) என எந்த வகையிலும் இருக்கலாம். ஒரு செயல்பாட்டிற்குத் தவறான வகை அல்லது வடிவத்திலான தரவு வழங்கப்பட்டால், அந்த செயல்பாடு எதிர்பார்த்தபடி செயல்படாமல் போகலாம். இதைத் தடுக்க, தரவு வகைகள் மற்றும் வடிவங்களைச் சரிபார்க்க Clojure-ல் உள்ளமைக்கப்பட்ட லைப்ரரிகள் உள்ளன, இவை ஸ்பெக்ஸ் (specs) என்று அழைக்கப்படுகின்றன. பெறப்பட்ட தரவு சரியாக உள்ளதா (முன்-நிபந்தனைகள் - pre-conditions) அல்லது திருப்பிக் கொடுக்கப்பட்ட தரவு எதிர்பார்த்த வடிவத்தில் உள்ளதா (பின்-நிபந்தனைகள் - post-conditions) என்பதைச் சரிபார்க்க நீங்கள் ஸ்பெக்ஸ்களைப் பயன்படுத்தலாம்.

ஸ்பெக்ஸ்களைப் பயன்படுத்த, முதலில் லைப்ரரியை அழைக்கவும் (require):

(require '[clojure.spec.alpha :as s])

நாம் clojure.spec.alpha-வை s என அழைத்துள்ளோம், எனவே அதன் செயல்பாடுகளை s/ முன்னொட்டுடன் (prefix) அழைக்கலாம் (உதாரணமாக, spec.alpha/valid?-க்கு பதிலாக s/valid?).

ஒரு மதிப்பு சரமா (string) என்று எப்படிச் சரிபார்ப்பது என்று பார்ப்போம்:

(s/valid? string? "Hello")

வெளியீடு (Output)

true

மேலே உள்ள உதாரணத்தில், நாம் s/valid?-ஐப் பயன்படுத்தி அதற்கு string? என்ற பிரடிகேட் செயல்பாட்டை (predicate function) வழங்குகிறோம். அதன் ஆர்கியுமெண்ட் ஒரு சரமாக இருந்தால் string? செயல்பாடு true-வை வழங்கும். "Hello" ஒரு சரம் என்பதால், s/valid? ஆனது true-வை வழங்குகிறது.

42 என்ற எண் சரமா என்று சரிபார்த்தால்:

(s/valid? string? 42)

வெளியீடு (Output)

false

(string? 42) என்பது false-ஐ வழங்குவதால், (s/valid? string? 42)-ம் false-ஐ வழங்குகிறது, இதனால் சரிபார்ப்பு தோல்வியடைகிறது.

அதேபோல், ஒரு மதிப்பு எண்ணா என்று சரிபார்க்கலாம்:

(s/valid? number? 42)

வெளியீடு (Output)

true

42 ஒரு எண் என்பதால், (number? 42) என்பது true-வை வழங்குகிறது, இதனால் (s/valid? number? 42) ஆனது true-வை வழங்குகிறது.

ஒரு மதிப்பு முழு எண்ணா (integer) என்றும் சரிபார்க்கலாம்:

(s/valid? integer? 123)

வெளியீடு (Output)

true

அல்லது அது ஒரு தசம எண்ணா (double) என்று பார்க்கலாம்:

(s/valid? double? 123.45)

வெளியீடு (Output)

true

123-ல் தசமப் புள்ளி இல்லாததால், தசம எண் சரிபார்ப்பு தோல்வியடைகிறது:

(s/valid? double? 123)

வெளியீடு (Output)

false

நாம் கலெக்ஷன்களையும் சரிபார்க்கலாம். உதாரணமாக, [1 2 3] ஒரு வெக்டரா என்று சரிபார்த்தல்:

(s/valid? vector? [1 2 3])

வெளியீடு (Output)

true

ஒரு மதிப்பு மேப்பா என்று சரிபார்த்தல்:

(s/valid? map? { 1 "one" 2 "two"})

வெளியீடு (Output)

true

ஒரு செட்டிற்குச் சரிபார்த்தல்:

(s/valid? set? #{:apple :orange})

வெளியீடு (Output)

true

ஒரு கீவேர்டிற்குச் சரிபார்த்தல்:

(s/valid? keyword? :ubuntu)

வெளியீடு (Output)

true

இப்போது சற்று சிக்கலான சூழலைக் கருதுவோம்: ஒரு கிரேடிங் சிஸ்டத்திற்கான சதவீதத்தைச் சரிபார்த்தல். ஒரு சரியான சதவீதம் 0 முதல் 100 வரை (உள்ளுட்பட) ஒரு எண்ணாக இருக்க வேண்டும். இதற்கு s/and-ஐப் பயன்படுத்தி ஒரு ஸ்பெக்கை வரையறுக்கலாம்:

(def valid-%? (s/and number? #(>= % 0) #(<= % 100)))

வெளியீடு (Output)

#'user/valid-%?

இந்த ஸ்பெக்கை உருவாக்க, மதிப்பு ஒரு எண்ணாக இருக்க வேண்டும்:

number?

மற்றும் அது பூஜ்ஜியத்தை விட அதிகமாகவோ அல்லது சமமாகவோ இருக்க வேண்டும். இந்தத் தேவைகளை இணைக்க s/and-ஐப் பயன்படுத்துகிறோம்:

(s/and
  number?
  #(>= % 0))

இறுதியாக, அது 100-ஐ விடக் குறைவாகவோ அல்லது சமமாகவோ இருக்க வேண்டும். s/and வடிவத்தில் மேலும் ஒரு பிரடிகேட்டைச் சேர்க்கிறோம்:

(s/and
  number?
  #(>= % 0)
  #(<= % 100))

இந்த ஸ்பெக்கை valid-%? என்ற பெயரில் ஒதுக்குகிறோம்:

(def valid-%?
  (s/and
    number?
    #(>= % 0)
    #(<= % 100)))

சுருக்கத்திற்காக, இதை ஒரே வரியில் வரையறுக்கலாம்:

(def valid-%? (s/and number? #(>= % 0) #(<= % 100)))

இப்போது நமது valid-%? ஸ்பெக்கைச் சோதிப்போம்:

(s/valid? valid-%? 85)

வெளியீடு (Output)

true

85 என்பது 0 மற்றும் 100-க்கு இடைப்பட்ட எண் என்பதால், இது சரியானது. இருப்பினும், இந்த கிரேடிங் சிஸ்டத்தில் 105% தவறானது:

(s/valid? valid-%? 105)

வெளியீடு (Output)

false

அதேபோல், -15 என்பது 0-வை விடக் குறைவு, எனவே இதுவும் false-ஐ வழங்குகிறது:

(s/valid? valid-%? -15)

வெளியீடு (Output)

false

பயிற்சி

பின்வருமாறு செயல்படும் string-or-keyword? என்ற ஸ்பெக்கை எழுதுங்கள்:

(s/valid? string-or-keyword? "abc") ;; true
(s/valid? string-or-keyword? :abc) ;; true
(s/valid? string-or-keyword? 42) ;; false

இந்தப் பயிற்சிக்காக, நீங்கள் https://clojuredocs.org/clojure.spec.alpha/or என்ற பக்கத்தைப் பார்க்கலாம்.

11.1. கலெக்ஷன்களை ஸ்பெக்கிங் செய்தல் (Specing Collections)

இந்தப் பகுதிக்கான வீடியோவை இங்கே காணலாம் https://yu7.in/clj-spec-col

கலெக்ஷன்களை எவ்வாறு ஸ்பெக் செய்வது என்று பார்ப்போம். கீழே உள்ள உதாரணத்தில், string-collection?-ஐ வரையறுக்கிறோம்:

(def string-collection? (s/coll-of string?))

வெளியீடு (Output)

#'user/string-collection?

s/coll-of ஸ்பெக், ஒரு கலெக்ஷனில் உள்ள ஒவ்வொரு உறுப்பும் வழங்கப்பட்ட ஸ்பெக்கிற்கு (இந்த வழக்கில், string?) இணங்குகிறதா என்று சரிபார்க்கிறது. சரங்களை உள்ளடக்கிய ஒரு வெக்டருக்கு string-collection?-ஐப் பயன்படுத்துவோம்:

(s/valid? string-collection? ["Hi" "Hello"])

வெளியீடு (Output)

true

"Hi" மற்றும் "Hello" ஆகிய இரண்டும் சரங்கள் என்பதால் இது வெற்றி பெறுகிறது. s/valid?-க்கு முதல் ஆர்கியுமெண்டாக ஸ்பெக்கையும் (string-collection?), இரண்டாவது ஆர்கியுமெண்டாக வெக்டரையும் (["Hi" "Hello"]) வழங்குகிறோம் என்பதைக் கவனியுங்கள்.

இப்போது சற்று சிக்கலான வழக்கை கையாளுவோம்: எண்களையோ அல்லது சரங்களையோ கொண்டிருக்கக்கூடிய ஒரு கலெக்ஷன்.

(def number-or-string? (s/or :number number? :string string?))

வெளியீடு (Output)

#'user/number-or-string?

மேலே உள்ள குறியீட்டில், நாம் s/or-ஐப் பயன்படுத்துகிறோம். டேக்குகள் (keywords) மற்றும் ஸ்பெக்குகளின் ஜோடிகளை வழங்குகிறோம்: :number number? மற்றும் :string string?. விளக்கத்தின் போது or ஸ்பெக்கின் எந்தப் பகுதி பொருந்தியது அல்லது தோல்வியடைந்தது என்பதைத் தெரிவிக்க இந்த டேக்குகள் பயனுள்ளதாக இருக்கும். வழங்கப்பட்ட ஸ்பெக்குகளில் ஏதேனும் ஒன்று பொருந்தினால் s/or ஸ்பெக் வெற்றி பெறும்.

ஒரு எண்ணைக் கொண்டு சோதித்தல்:

(s/valid? number-or-string? 1)

வெளியீடு (Output)

true

ஒரு சரத்தைக் கொண்டு சோதித்தல்:

(s/valid? number-or-string? "Hi")

வெளியீடு (Output)

true

ஒரு கலெக்ஷன் எண்களை அல்லது சரங்களை மட்டுமே கொண்டுள்ளதா என்று சரிபார்க்க, number-or-string?-ஐ s/coll-of-ல் இணைப்போம்:

(def number-or-string-collection? (s/coll-of number-or-string?))

வெளியீடு (Output)

#'user/number-or-string-collection?

இப்போது number-or-string-collection? கலெக்ஷனில் உள்ள ஒவ்வொரு உறுப்பும் ஒரு எண்ணாகவோ அல்லது சரமாகவோ இருப்பதை உறுதி செய்யும்.

பின்வரும் வெக்டரில் எண்களும் சரங்களும் மட்டுமே உள்ளதால் இது வெற்றி பெறுகிறது:

(s/valid? number-or-string-collection? [1 "Hi" "India" 42])

வெளியீடு (Output)

true

பின்வரும் வெக்டரில் ஒரு கீவேர்ட் (:keyword) உள்ளதால் இது தோல்வியடைகிறது:

(s/valid? number-or-string-collection? [1 "Hi" "India" 42 :keyword])

வெளியீடு (Output)

false

11.2. கலெக்ஷன்களை ஆய்வு செய்தல் (Inspecting Collections)

இந்தப் பகுதிக்கான வீடியோவை இங்கே காணலாம் https://yu7.in/clj-collections

ஒரு கலெக்ஷனுக்குள் உள்ள தரவை எவ்வாறு சரிபார்ப்பது என்று பார்ப்போம். ஒரு நபரின் விவரங்களைக் கொண்ட ஒரு வெக்டர் இருப்பதாகக் கொள்வோம்: பெயர், வயது மற்றும் பாலினம்.

பெயர் ஒரு சரமாக இருக்க வேண்டும்: :name string?

வயது ஒரு எண்ணாக இருக்க வேண்டும்: :name string? :age number?

பாலினம் ஒரு கீவேர்டால் குறிக்கப்படலாம்: :name string? :age number? :gender keyword?

இந்த உறுப்புகள் சரியாக இந்த வரிசையில் இருப்பதை உறுதி செய்ய நாம் s/cat [5] (concatenate) ஸ்பெக்கைப் பயன்படுத்தலாம்:

(s/cat :name string? :age number? :gender keyword?)

இந்த ஸ்பெக்கிற்கு valid-person-vector? என்று பெயரிடுவோம்:

(def valid-person-vector? (s/cat :name string? :age number? :gender keyword?))

வெளியீடு (Output)

user/valid-person-vector?

ஒரு சரம், ஒரு எண் மற்றும் ஒரு கீவேர்ட் கொண்ட வெக்டரை வழங்கினால், அது வெற்றி பெறும்:

(s/valid? valid-person-vector? ["Karthik" 40 :male])

வெளியீடு (Output)

true

மற்றபடி, இது தோல்வியடையும்.

இப்போது மேப்களைச் சரிபார்ப்பதைப் பார்ப்போம். ஒரு மேப் குறிப்பிட்ட கீகளைக் கொண்டுள்ளதா என்பதை உறுதி செய்ய s/keys [6] ஸ்பெக்கைப் பயன்படுத்தலாம்.

(def valid-person-map?
  (s/keys :req-un [::name
                   ::age
                   ::gender]))

வெளியீடு (Output)

#'user/valid-person-map?

s/keys-ல், :req-un ஆர்கியுமெண்ட் required and unique (தேவையான மற்றும் தனித்துவமான) என்பதைக் குறிக்கிறது. இதன் பொருள் மேப் இந்த கீகளைக் கொண்டிருக்க வேண்டும், மேலும் இங்கே இந்த கீகள் மட்டுமே சரிபார்க்கப்படுகின்றன.

இரட்டை கோலன் தொடரியல் (உதாரணமாக, ::name) என்பது நேம்ஸ்பேஸ் செய்யப்பட்ட கீவேர்டிற்கான (namespaced keyword) குறுக்குவழி ஆகும். இது தற்போதைய நேம்ஸ்பேஸில் உள்ள :name என்ற கீவேர்டைப் பயன்படுத்த Clojure-க்கு சொல்கிறது.

இப்போது valid-person-map?-ஐப் பயன்படுத்தி ஒரு மேப்பைச் சரிபார்க்கலாம்:

(s/valid? valid-person-map? {:name "Karthik"
                             :age 40
                             :gender :male})

வெளியீடு (Output)

true

தேவையான கீகளான :name, :age, மற்றும் :gender ஆகியவை உள்ளதால் இந்த மேப் வெற்றி பெறுகிறது.

11.3. மேப் மதிப்புகளைச் சரிபார்த்தல் (Validating Map Values)

இந்தப் பகுதிக்கான வீடியோவை இங்கே காணலாம் https://yu7.in/clj-map-spec

மேப்களைச் சரிபார்க்கும்போது, குறிப்பிட்ட கீகள் இருப்பதையும் அவற்றின் தொடர்புடைய மதிப்புகள் சில விதிகளைப் பின்பற்றுவதையும் நாம் உறுதி செய்ய விரும்புவோம்.

முதலில், s/def-ஐப் பயன்படுத்தி தனித்தனி கீகளுக்கு ஸ்பெக்குகளை வரையறுக்கலாம். உதாரணமாக, :name கீ ஒரு சரத்திற்கு மேப் செய்யப்பட வேண்டும்:

(s/def ::name string?)

வெளியீடு (Output)

:user/name

:age கீ ஒரு முழு எண்ணிற்கு (integer) மேப் செய்யப்பட வேண்டும்:

(s/def ::age int?)

வெளியீடு (Output)

:user/age

:gender கீ ஒரு கீவேர்டிற்கு மேப் செய்யப்பட வேண்டும்:

(s/def ::gender keyword?)

வெளியீடு (Output)

:user/gender

இப்போது இவற்றை s/keys-ஐப் பயன்படுத்தி ஒரு நபருக்கான ஒற்றை ஸ்பெக்காக இணைக்கலாம்:

(s/def ::person (s/keys :req-un [::name ::age ::gender]))

வெளியீடு (Output)

:user/person

::person ஸ்பெக் இப்போது அது சரிபார்க்கும் எந்தவொரு மேப்பும் :name (சரம்), :age (முழு எண்), மற்றும் :gender (கீவேர்ட்) ஆகிய கீகளைக் கொண்டிருப்பதை உறுதி செய்கிறது.

இதைச் சோதிப்போம்:

(s/valid? ::person {:name "Karthik"
                    :age 40
                    :gender :male})

வெளியீடு (Output)

true

:age-ஐ "40" என்ற சரமாக மாற்றினால், இது தோல்வியடையும்:

(s/valid? ::person {:name "Karthik"
                    :age "40"
                    :gender :male})

வெளியீடு (Output)

false

11.4. ஸ்பெக்ஸ்களை விளக்குதல் (Explaining Specs)

இந்தப் பகுதிக்கான வீடியோவை இங்கே காணலாம் https://yu7.in/clj-exp-spec

ஒரு மதிப்பு ஏன் ஸ்பெக் சரிபார்ப்பில் தோல்வியடைந்தது என்பதற்கான மனிதனால் படிக்கக்கூடிய விளக்கத்தை s/explain செயல்பாடு வழங்குகிறது.

உதாரணமாக:

(s/explain number? "42")

வெளியீடு (Output)

"42" - failed: number?

ஒரு ஸ்பெக் வெற்றி பெறும்போது, s/explain எளிமையாக Success! என்று வழங்கும்:

(s/explain number? 42)

வெளியீடு (Output)

Success!
(s/explain number-or-string? "56")

வெளியீடு (Output)

Success!

ஒரு கீவேர்டை number-or-string? ஸ்பெக் கொண்டு சோதித்தால், or ஸ்பெக்கின் எந்தப் பகுதிகள் தோல்வியடைந்தன என்பதை நாம் தெளிவாகக் காணலாம்:

(s/explain number-or-string? :56)

வெளியீடு (Output)

:56 - failed: number? at: [:number]
:56 - failed: string? at: [:string]
nil

மதிப்பு ஒரு எண்ணாக இருந்தால், அது வெற்றி பெறும்:

(s/explain number-or-string? 56)

வெளியீடு (Output)

Success!
nil

11.5. உடன்பாடு (Conformity)

இந்தப் பகுதிக்கான வீடியோவை இங்கே காணலாம் https://yu7.in/clj-spec-fun

s/conform செயல்பாடு தரவு ஒரு ஸ்பெக்கு இணங்குகிறதா என்று சரிபார்க்கிறது. அப்படி இருந்தால், அது அந்த மதிப்பையே திருப்பிக் கொடுக்கும்:

(s/conform number? 42)

வெளியீடு (Output)

42

தரவு ஸ்பெக்கு இணங்கவில்லை என்றால், s/conform ஒரு clojure.spec.alpha/invalid எக்ஸெப்ஷனை (exception) ஏற்படுத்தும்:

(s/conform number? "42")

வெளியீடு (Output)

clojure.spec.alpha/invalid
(s/conform number-or-string? :56)

வெளியீடு (Output)

:clojure.spec.alpha/invalid

11.6. செயல்பாடுகளில் ஸ்பெக்ஸ்களைப் பயன்படுத்துதல் (Using Specs in Functions)

இந்தப் பகுதிக்கான வீடியோவை இங்கே காணலாம் https://yu7.in/clj-spec-fun

ஸ்பெக்ஸ்களை ஏன் பயன்படுத்த வேண்டும்? Clojure-ல், நாம் பொதுவாக சிக்கலான ஆப்ஜெக்ட்களுக்குப் பதிலாக எளிய தரவு வகைகளை (வெக்டர்கள், மேப்கள், செட்கள்) பயன்படுத்துகிறோம். செயல்பாடுகள் இந்த எளிய வகைகளை நம்பியிருக்கும்போது, தரவின் முழுமையையும் உடன்பாட்டையும் சரிபார்ப்பது முக்கியமாகிறது. ஸ்பெக்ஸ் இதைச் செய்ய ஒரு சக்திவாய்ந்த வழியை வழங்குகின்றன.

பின்வரும் உதாரணத்தைக் கவனியுங்கள்:

(defn add-two-numbers [a b]
  {:pre [(s/valid? number? a)
         (s/valid? number? b)]}
  (+ a b))

சரியான எண்களைக் கொண்டு செயல்பாட்டை அழைத்தல்:

(add-two-numbers 3 5)

வெளியீடு (Output)

8

தவறான ஆர்கியுமெண்ட்டைக் கொண்டு அழைத்தல்:

(add-two-numbers 3 "5")

வெளியீடு (Output)

; Execution error (AssertionError) at user/add-two-numbers (REPL:100).
; Assert failed: (s/valid? number? b)

:pre ஹூக் ஆர்கியுமெண்டுகள் a மற்றும் b எண்களா என்று சரிபார்க்கிறது. சரிபார்ப்பு தோல்வியுற்றால், செயல்பாட்டின் உடல் இயங்காது.

அதேபோல், திருப்பிக் கொடுக்கப்படும் தரவின் முழுமையை உறுதி செய்ய :post ஹூக்கைப் பயன்படுத்தலாம், இதன் மூலம் உங்கள் அப்ளிகேஷனின் அடுத்தடுத்த நிலைகளில் முடிவுகள் பிரச்சனைகளை ஏற்படுத்தாமல் இருப்பதை உறுதி செய்யலாம்.

12. ஆட்டம்ஸ் (Atoms)

இந்தப் பகுதிக்கு, நீங்கள் https://gitlab.com/clojure-book/code/-/raw/master/atom.clj-ல் உள்ள குறியீட்டைப் பயன்படுத்தலாம். உங்கள் REPL டெர்மினலில் இயங்கினாலும் அல்லது உங்கள் IDE-உடன் ஒருங்கிணைக்கப்பட்டிருந்தாலும், வரிகளை ஒவ்வொன்றாக நகலெடுத்து (copy) பேஸ்ட் (paste) செய்யவும்.
இந்தப் பகுதிக்கான வீடியோவை இங்கே காணலாம் https://yu7.in/clj-atom

Clojure ஒரு ஃபங்க்ஷனல் புரோகிராமிங் (functional programming) மொழி, அதாவது இது முதன்மையாக செயல்பாடுகளைக் (functions) கையாள்கிறது. உதாரணமாக, \$f(x) = 2x + 3\$ என்பது ஒரு செயல்பாடாகும். இது எதையும் நினைவில் கொள்வதில்லை. நீங்கள் ஒரு மதிப்பை, உதாரணமாக \$x = 7\$-ஐ வழங்கினால், உங்களுக்கு \$17\$ என்ற வெளியீடு கிடைக்கும். அவ்வளவுதான்; இது ஒரு தூய செயல்பாடு (pure function).

இருப்பினும், நிஜ உலகில், நீங்கள் நிலையை (state) சேமிக்க வேண்டும், அதற்கு Clojure ஒரு வழியை வழங்குகிறது. இது மொழியில் ஒருவித அசுத்தத்தை (impurity) அறிமுகப்படுத்துகிறது. ஒரு கடுமையான தூய ஃபங்க்ஷனல் மொழியில், அழைப்புகளுக்கு இடையே நிலையைப் பராமரிக்க எந்த வழியும் இருக்காது. அடிப்படையில், முழுமையாக தூய ஒரு நிரலால் வெளி உலகத்துடன் தொடர்பு கொள்ள முடியாது, இது அதை நடைமுறையில் பயனற்றதாக்கும்.

Clojure செயல்பாட்டு அழைப்புகளுக்கு குறுக்கே நினைவகத்தில் மதிப்புகளைச் சேமிக்க ஒரு வழியை வழங்குகிறது; இவை ஆட்டம்ஸ் (atoms) என்று அழைக்கப்படுகின்றன. இவற்றை ஒரு அமைப்பை இயக்குவதற்குத் தேவையான ரயில்வே, பேருந்து மற்றும் விமான கால அட்டவணைகள் போன்ற அத்தியாவசிய உண்மைகளைச் சேமிக்கும் ஒரு உலகளாவிய நூலகமாக நினையுங்கள்.

தொடங்குவதற்கு, கீழே காட்டியுள்ளபடி ஒரு ஆட்டமை வரையறுப்போம்:

(def counter (atom 0))

Clojure-ன் பெரும்பாலான வரையறைகளைப் போலவே, நாம் (def …​) என்பதில் தொடங்குகிறோம், அதைத் தொடர்ந்து ஆட்டமின் பெயர் வருகிறது. இந்த வழக்கில், நாம் அதை counter என்று அழைக்கிறோம்:

(def counter ....)

இது ஒரு சாதாரண மாறி அல்ல, மாறாக ஒரு ஆட்டம் என்று நாம் Clojure-க்கு சொல்ல வேண்டும். இதைச் செய்ய, நாம் atom செயல்பாட்டைப் பயன்படுத்துகிறோம்:

(def counter (atom .......))

இப்போது நாம் ஒரு ஆரம்ப மதிப்பை வழங்குகிறோம்; அதற்கு 0-வை ஒதுக்குவோம்:

(def counter (atom 0))

இப்போது நாம் நமது counter ஆட்டமை வரையறுத்துவிட்டோம்.

Clojure-ல் சேமிப்பு விஷயங்களை ஏன் ஆட்டம் என்று அழைக்கிறார்கள் என்று எனக்குத் தெரியவில்லை.

ஒரு ஆட்டமின் மதிப்பை மீட்டெடுக்க, நாம் @ (dereference) ஆபரேட்டரைப் பயன்படுத்துகிறோம். counter ஆட்டமின் மதிப்பைப் பெற, நாம் இவ்வாறு பயன்படுத்துகிறோம்:

@counter

வெளியீடு (Output)

0

இது சரியாக 0-வை வழங்குகிறது.

இருப்பினும், நாம் @ ஆபரேட்டரை பயன்படுத்தாமல் counter-ஐக் குறிப்பிட்டால், அது அதன் மதிப்பை வழங்காமல் ஆட்டம் ஆப்ஜெக்ட்டையே (atom object) வழங்கும்:

counter

வெளியீடு (Output)

#object[clojure.lang.Atom 0x537d2c4f {:status :ready, :val 0}]

swap! செயல்பாடு ஒரு ஆட்டமைப் புதுப்பிக்க நமக்கு அனுமதிக்கிறது. இது தனது இரண்டாவது ஆர்கியுமெண்டாக ஒரு செயல்பாட்டை ஏற்கிறது, இது ஆட்டமின் மதிப்பு எவ்வாறு மாற்றப்பட வேண்டும் என்பதை வரையறுக்கிறது. கீழே உள்ள உதாரணத்தில், இரண்டாவது ஆர்கியுமெண்ட் inc செயல்பாடாகும், இது ஆட்டமின் மதிப்பை அதிகரிக்கிறது:

(swap! counter inc)

வெளியீடு (Output)

1

இயக்கப்பட்டவுடன், @counter-ன் மதிப்பு 1 ஆகிறது. பின்வரும் குறியீட்டை இயக்குவதன் மூலம் இதை நாம் சரிபார்க்கலாம்:

@counter

வெளியீடு (Output)

1

reset! செயல்பாட்டைப் பயன்படுத்தி ஒரு ஆட்டமின் மதிப்பை எந்தவொரு குறிப்பிட்ட மதிப்பிற்கும் மாற்றலாம். கீழே, நாம் counter-ன் மதிப்பை 42 என மாற்றுகிறோம்:

(reset! counter 42)

வெளியீடு (Output)

42
@counter

வெளியீடு (Output)

42

இப்போது, உருவாக்கப்பட்ட ரோபோக்களின் எண்ணிக்கையைத் தொடர்ந்து கண்காணிக்க robo-count என்ற ஆட்டமை வரையறுப்போம்:

(def robo-count (atom 0))

இப்போது, make-robot என்ற செயல்பாட்டை வரையறுப்போம்:

(defn make-robot [])

make-robot அழைக்கப்படும்போதெல்லாம், அது robo-count-ஐ 1 அதிகரிக்கும்:

(defn make-robot []
  (swap! robo-count inc))

இறுதியாக, உருவாக்கப்பட்ட மொத்த ரோபோக்களின் எண்ணிக்கையைக் குறிக்கும் ஒரு சரத்தை (string) திருப்பிக் கொடுப்போம்:

(defn make-robot []
  (swap! robo-count inc)
  (str "Made a robot. Total robots: " @robo-count))

இதை இயக்குவோம்:

(make-robot)

வெளியீடு (Output)

"Made a robot. Total robots: 1"

இதை மேலும் இரண்டு முறை இயக்குவோம்:

(make-robot)

வெளியீடு (Output)

"Made a robot. Total robots: 2"
(make-robot)

வெளியீடு (Output)

"Made a robot. Total robots: 3"

உலகளாவிய robo-count ஆட்டம் மொத்த ரோபோக்களின் எண்ணிக்கையைத் தொடர்ந்து கண்காணிக்கிறது. இது குறியீட்டில் எங்கும் அணுகக்கூடியது என்பதால், அதன் மதிப்பை இவ்வாறு அச்சிடலாம்:

(println (str "Robots made: " @robo-count))

வெளியீடு (Output)

Robots made: 3

13. Clojure குறியீடு என்றால் என்ன? (What is Clojure code)

இந்தப் பகுதிக்கான வீடியோவை இங்கே காணலாம்: https://yu7.in/clj-code

Clojure என்பது Lisp-ன் ஒரு கிளைமொழியாகும் (dialect). Lisp என்பதற்கு "பட்டியல் செயலாக்கம்" (List Processing) என்று பொருள். Clojure-ல், கிட்டத்தட்ட அனைத்துக் குறியீடுகளும் ஒரு பட்டியல் தரவு கட்டமைப்பாகவே (list data structure) பிரதிநிதித்துவப்படுத்தப்படுகின்றன. Lisp-ல், ஒரு நிரல் என்பது ஒரு தரவு கட்டமைப்பு, மற்றும் ஒரு தரவு கட்டமைப்பை ஒரு நிரலாகக் கருத முடியும், இது அதற்கு மிகப்பெரிய சக்தியை வழங்குகிறது.

Clojure-ன் குறியீடு ஒரு நிலையான வடிவத்தைப் பின்பற்றுகிறது:

(function-name argument-1 .... argument-n)

மேலே பார்த்தது போல, அனைத்தும் ( மற்றும் ) அடைப்புக்குறிகளுக்குள் உள்ளன, இது ஒரு பட்டியல் தரவு கட்டமைப்பைக் குறிக்கிறது. ஒரு பட்டியல்-செயலாக்க மொழி என்பதால், இத்தகைய கட்டமைப்புகளைச் செயலாக்குவதற்காகவே Clojure சிறப்பாக வடிவமைக்கப்பட்டுள்ளது.

பின்வரும் உதாரணத்தைக் consider செய்வோம்:

(* 5 (+ 1 2 3))

மேலே உள்ள பட்டியலை இவ்வாறு பிரதிநிதித்துவப்படுத்தலாம்: * செயல்பாடு இரண்டு ஆர்கியுமெண்டுகளைப் பெறுகிறது—முதலாவதாக 5 மற்றும் இரண்டாவதாக (+ 1 2 3). இதை வரைபடமாக இவ்வாறு காட்டலாம்:

what is clojure code 1

இப்போது ஆழமான நீல நிறப் புள்ளிகளைப் பாருங்கள்: 1, 2, மற்றும் 3. அவை + செயல்பாட்டிற்கு வழங்கப்பட்டு 6 ஆகக் குறைக்கப்படுகின்றன, இதன் விளைவாக ஒரு புதிய தரவு கட்டமைப்பு உருவாகிறது:

what is clojure code 2

இதை இவ்வாறு எழுதலாம்:

(* 5 6)

இந்த வெளிப்பாட்டில், 5 மற்றும் 6 ஆகிய இரண்டும் * செயல்பாட்டிற்கு வழங்கப்படுகின்றன, அது அவற்றை பெருக்கி 30 என்ற முடிவைத் தருகிறது.

what is clojure code 3

Clojure ஒரு Lisp என்பதால், அதன் குறியீட்டில் சிறந்த சீரான தன்மை (uniformity) உள்ளது. ஆப்ஜெக்ட்-ஓரியண்டட் (Object-Oriented) மொழிகளைப் போலல்லாமல், நீங்கள் சிக்கலான தொடரியலை (syntax) நினைவில் வைத்திருக்க வேண்டிய அவசியமில்லை; இது உங்கள் மூளைக்கு எளிமையானது, இதனால் உங்கள் நியூரான்கள் (neurons) வணிகச் சிக்கல்களைத் தீர்ப்பதில் கவனம் செலுத்த முடியும்.

14. வரிசை (Sequence)

இந்தப் பகுதிக்கு, நீங்கள் https://gitlab.com/clojure-book/code/-/raw/master/sequence.clj-ல் உள்ள குறியீட்டைப் பயன்படுத்தலாம். உங்கள் REPL டெர்மினலில் இயங்கினாலும் அல்லது உங்கள் IDE-உடன் ஒருங்கிணைக்கப்பட்டிருந்தாலும், வரிகளை ஒவ்வொன்றாக நகலெடுத்து (copy) பேஸ்ட் (paste) செய்யவும்.
இந்தப் பகுதிக்கான வீடியோவை இங்கே காணலாம் https://yu7.in/clj-sequence

Clojure-ல், count ஒரு பட்டியலில் உள்ள உறுப்புகளின் எண்ணிக்கையைக் கணக்கிடுகிறது.

(count '(1 2 3 4))

வெளியீடு (Output)

4

இது ஒரு வெக்டரில் உள்ள உறுப்புகளின் எண்ணிக்கையைக் கணக்கிடுகிறது.

(count [1 2 3 4])

வெளியீடு (Output)

4

இது செட்களுக்கும் (sets) வேலை செய்கிறது.

(count #{1 2 3 4})

வெளியீடு (Output)

4

மேப்களுக்கும் (maps) பொருந்தும்.

(count {:one 1 :two 2 :three 3 :four 4})

வெளியீடு (Output)

4

ஆர்கியுமெண்டின் வகையை count சரிபார்த்து ஒவ்வொரு வகைக்கும் ஒரு தனி அல்காரிதத்தை அழைக்கிறதா, அல்லது எண்ணுவதற்கு முன் ஆர்கியுமெண்ட்டை ஒரு பொதுவான தரவு வகையாக மாற்றுகிறதா என்று நீங்கள் நினைக்கலாம்.

seq [7] என்ற ஒரு செயல்பாடு உள்ளது, இது மேலே உள்ள அனைத்து தரவு வகைகளையும் ஒரு வரிசையாக (sequence) மாற்றுகிறது:

(seq '(1 2 3 4))

வெளியீடு (Output)

(1 2 3 4)
(seq [1 2 3 4])

வெளியீடு (Output)

(1 2 3 4)
(seq #{1 2 3 4})

வெளியீடு (Output)

(1 4 3 2)
(seq {:one 1 :two 2 :three 3 :four 4})

வெளியீடு (Output)

([:one 1] [:two 2] [:three 3] [:four 4])

இப்போது count செய்ய வேண்டியதெல்லாம், கீழே காட்டப்பட்டுள்ளபடி ஒரு வரிசையில் உள்ள உறுப்புகளை எண்ணுவது மட்டுமே:

(count '(1 2 3 4))

வெளியீடு (Output)

4
(count '([:one 1] [:two 2] [:three 3] [:four 4]))

வெளியீடு (Output)

4

இப்போது partition மற்றும் கலெக்ஷன்களில் இயங்கும் மற்ற செயல்பாடுகளைப் பற்றி சிந்தித்து, நீங்களே முயற்சி செய்து பாருங்கள்:

(seq [1 2 3])

வெளியீடு (Output)


(seq '(1 2 3))

வெளியீடு (Output)


(seq #{1 2 3})

வெளியீடு (Output)


(seq {1 "one" 2 "two" 3 "three"})

வெளியீடு (Output)


(partition 2 {1 "one" 2 "two" 3 "three" 4 "four"})

வெளியீடு (Output)


(partition 2 (seq {1 "one" 2 "two" 3 "three" 4 "four"}))

வெளியீடு (Output)


(partition 2 '(1 2 3 4))

வெளியீடு (Output)


(partition 2 (seq '(1 2 3 4)))

வெளியீடு (Output)


(partition 2 #{1 2 3 4})

வெளியீடு (Output)


(partition 2 (seq #{1 2 3 4}))

வெளியீடு (Output)


(partition 2 [1 2 3 4])

வெளியீடு (Output)


(partition 2 (seq [1 2 3 4]))

வெளியீடு (Output)


partition பல அல்காரிதங்களைச் செயல்படுத்துகிறது என்று நினைக்கிறீர்களா, உதாரணமாக பட்டியலுக்கு ஒன்று, வெக்டருக்கு ஒன்று என? அல்லது இது இன்னும் புத்திசாலித்தனமான செயல்பாட்டைக் கொண்டுள்ளதா?

15. லேசி சீக்வென்ஸ் (Lazy Sequence)

இந்தப் பகுதிக்கான வீடியோவை இங்கே காணலாம் https://yu7.in/clj-lzseq

சில வரிசைகள் (sequences) தேவைப்படும்போது மட்டுமே மதிப்பீடு செய்யப்படுகின்றன, இவை லேசி சீக்வென்ஸ்கள் (lazy sequences) என்று அழைக்கப்படுகின்றன. உதாரணமாக, உங்கள் கணினி செயலிழக்காமல் (crash) மிக எளிதாக முடிவிலி எண்களின் (infinite numbers) லேசி சீக்வென்ஸை உங்களால் உருவாக்க முடியும்.

இதை இவ்வாறு நினைத்துக்கொள்ளுங்கள்: யாராவது "முடிவிலி" (infinity) என்று குறிப்பிடும்போது, உங்கள் மூளை செயலிழப்பதில்லை; அது முடிவிலி என்ற கருத்தையும் அது கணிதத்தில் எவ்வாறு பொருந்துகிறது என்பதையும் எளிதாகப் புரிந்துகொள்கிறது. கால்குலஸ் (calculus) போன்ற ஒரு குறிப்பிட்ட சூழலில் நீங்கள் அதைப் பயன்படுத்த வேண்டியிருக்கும் போது, உங்களால் அதைத் தொடர்ந்து பயன்படுத்த முடியும்.

15.1. repeat

இந்தப் பகுதிக்கு, நீங்கள் https://gitlab.com/clojure-book/code/-/raw/master/lazy_sequence_repeat.clj-ல் உள்ள குறியீட்டைப் பயன்படுத்தலாம். உங்கள் REPL டெர்மினலில் இயங்கினாலும் அல்லது உங்கள் IDE-உடன் ஒருங்கிணைக்கப்பட்டிருந்தாலும், வரிகளை ஒவ்வொன்றாக நகலெடுத்து (copy) பேஸ்ட் (paste) செய்யவும்.

repeat செயல்பாட்டைப் பார்ப்போம். கீழே உள்ள குறியீட்டை கவனியுங்கள்:

(def multiverse (repeat "universe"))

மேலே உள்ள குறியீட்டில், நாம் multiverse என்ற மாறியை உருவாக்குகிறோம், அதில் "universe" என்ற வார்த்தையின் பல பிரதிகள் உள்ளன; கோட்பாட்டளவில், இது முடிவிலியானது. இதை ஒரு வரிசையாகக் கருதி, அதன் முதல் உறுப்பை எடுப்போம்:

(first multiverse)

வெளியீடு (Output)

"universe"

நமக்கு "universe" கிடைக்கிறது. இப்போது அதிலிருந்து 5 உறுப்புகளை எடுப்போம்:

(take 5 multiverse)

வெளியீடு (Output)

("universe" "universe" "universe" "universe" "universe")

நமக்கு 5 யுனிவர்ஸ்கள் கிடைக்கின்றன. இப்போது இந்த வரிசையின் மீதிப்பகுதியை (rest) எடுப்போம்:

(rest multiverse) ;; இது வேலை செய்யாது போல் தெரிகிறது

இது ஒரு பிழையை ஏற்படுத்தும். ஏனெனில், ஒரு முடிவிலி வரிசையின் மீதிப்பகுதியை உங்களால் எப்படி எடுக்க முடியும்?

அதேபோல், last செயல்பாடும் பிழையை ஏற்படுத்தும்; ஒரு முடிவிலி வரிசையின் கடைசி உறுப்பை உங்களால் எப்படி எடுக்க முடியும்?

(last multiverse) ;; இது வேலை செய்யாது போல் தெரிகிறது

இப்போது 10,001-வது உறுப்பை எடுப்போம்:

(nth multiverse 10000)

வெளியீடு (Output)

"universe"

இப்போது 101-வது உறுப்பை எடுப்போம்:

(nth multiverse 100)

வெளியீடு (Output)

"universe"

repeat செயல்பாடு முடிவிலி வரிசைகளை மட்டுமே உருவாக்குவதில்லை; Clojure-க்கு எத்தனை முறை மீண்டும் செய்ய வேண்டும் என்று கூறுவதன் மூலம் ஒரு வரையறுக்கப்பட்ட (finite) வரிசையை உருவாக்க முடியும். கீழே உள்ள குறியீடு "universe"-ன் ஐந்து பிரதிகளை மட்டுமே உருவாக்குகிறது:

(repeat 5 "universe")

வெளியீடு (Output)

("universe" "universe" "universe" "universe" "universe")

நாம் 7-வது உறுப்பை அணுக முயற்சித்தால், அது ஒரு விதிவிலக்கை (exception) ஏற்படுத்தும்:

(nth (repeat 5 "universe") 6)

வெளியீடு (Output)

; Execution error (IndexOutOfBoundsException) at user/eval2056 (REPL:11).
; null

(repeat 5 "universe")-ல் 5 உறுப்புகள் மட்டுமே இருப்பதால், கீழே உள்ள குறியீடு 4-வது உறுப்பை மீட்டெடுக்கிறது, இது சரியாக வேலை செய்யும்.

(nth (repeat 5 "universe") 3)

வெளியீடு (Output)

"universe"

(repeat 5 "universe")-ல் வரையறுக்கப்பட்ட எண்ணிக்கையிலான உறுப்புகள் இருப்பதால், rest செயல்பாட்டைப் பயன்படுத்தும் போது எந்தப் பிழையும் ஏற்படாது:

(rest (repeat 5 "universe"))

வெளியீடு (Output)

("universe" "universe" "universe" "universe")

அதேபோல், last செயல்பாடும் இதில் வேலை செய்யும்:

(last (repeat 5 "universe"))

வெளியீடு (Output)

"universe"

15.2. cycle

இந்தப் பகுதிக்கு, நீங்கள் https://gitlab.com/clojure-book/code/-/raw/master/lazy_sequence_cycle.clj-ல் உள்ள குறியீட்டைப் பயன்படுத்தலாம். உங்கள் REPL டெர்மினலில் இயங்கினாலும் அல்லது உங்கள் IDE-உடன் ஒருங்கிணைக்கப்பட்டிருந்தாலும், வரிகளை ஒவ்வொன்றாக நகலெடுத்து (copy) பேஸ்ட் (paste) செய்யவும்.
இந்தப் பகுதிக்கான வீடியோவை இங்கே காணலாம் https://yu7.in/clj-cycle

நீங்கள் ஒரு வரிசையை சுழற்சி முறையில் (cyclical manner) அணுக விரும்பினால், அதற்கு cycle செயல்பாட்டைப் பயன்படுத்த வேண்டும். கீழே உள்ள குறியீட்டை கவனியுங்கள்:

(def multiverse (cycle ["universe" "antiverse"]))

வெளியீடு (Output)

#'user/multiverse

மேலே உள்ள குறியீடு ஒரு முடிவிலி வரிசையை உருவாக்கி அதை multiverse என்ற மாறியில் ஒதுக்குகிறது. இதில் ஒவ்வொரு இரட்டைப்படை இண்டெக்ஸ் உறுப்பும் (0, 2, 4, …​) "universe" என்றும், ஒவ்வொரு ஒற்றைப்படை இண்டெக்ஸ் உறுப்பும் (1, 3, 5, …​) "antiverse" என்றும் இருக்கும். எனவே, முதல் உறுப்பு "universe" ஆகும்:

(first multiverse)

வெளியீடு (Output)

"universe"

இரண்டாவது உறுப்பு "antiverse" ஆகும்:

(second multiverse)

வெளியீடு (Output)

"antiverse"

நாம் 5 உறுப்புகளை எடுத்தால், அவை "universe" மற்றும் "antiverse" ஆகியவற்றுக்கு இடையில் மாறி மாறி வரும், அது இவ்வாறு இருக்கும்:

(take 5 multiverse)

வெளியீடு (Output)

("universe" "antiverse" "universe" "antiverse" "universe")

எந்தவொரு இரட்டைப்படை இண்டெக்ஸ் உறுப்பையும் நாம் எடுத்தால், அது "universe" ஆக இருக்கும்:

(nth multiverse 2000)

வெளியீடு (Output)

"universe"

அதேபோல், எந்தவொரு ஒற்றைப்படை இண்டெக்ஸ் உறுப்பும் "antiverse" ஆக இருக்கும்:

(nth multiverse 1783)

வெளியீடு (Output)

"antiverse"

இப்போது சில தென்னிந்திய டிஃபின் (tiffin) உணவுகளைச் சுழற்சி முறையில் அமைப்போம்:

(def tiffin-items
  (cycle ["idli", "vadai", "dosai", "sambar"]))

வெளியீடு (Output)

#'user/tiffin-items

6-வது உறுப்பை எடுப்போம்:

(nth tiffin-items 5)

வெளியீடு (Output)

"vadai"

கடைசி உறுப்பான "sambar", இண்டெக்ஸ் 3-ல் உள்ளது. எனவே, 0-வது உறுப்பு இண்டெக்ஸ் 4-ல் மீண்டும் வரும், 1-வது உறுப்பு இண்டெக்ஸ் 5-ல் வரும், இப்படியே ஒரு முடிவில்லாத லூப் உருவாகிறது.

இப்போது tiffin-items-ல் 10 உறுப்புகளை எடுத்து அது எவ்வாறு மீண்டும் மீண்டும் வருகிறது என்று பார்ப்போம்:

(take 10 tiffin-items)

வெளியீடு (Output)

("idli" "vadai" "dosai" "sambar" "idli" "vadai" "dosai" "sambar" "idli" "vadai")

15.3. iterate

இந்தப் பகுதிக்கு, நீங்கள் https://gitlab.com/clojure-book/code/-/raw/master/lazy_sequence_iterate.clj-ல் உள்ள குறியீட்டைப் பயன்படுத்தலாம். உங்கள் REPL டெர்மினலில் இயங்கினாலும் அல்லது உங்கள் IDE-உடன் ஒருங்கிணைக்கப்பட்டிருந்தாலும், வரிகளை ஒவ்வொன்றாக நகலெடுத்து (copy) பேஸ்ட் (paste) செய்யவும்.
இந்தப் பகுதிக்கான வீடியோவை இங்கே காணலாம் https://yu7.in/clj-iterate

iterate என்பது Clojure-ல் உள்ள ஒரு சக்திவாய்ந்த செயல்பாடாகும். ஒரு உதாரணத்தைப் பார்ப்போம்:

(def all-numbers (iterate inc 1))

வெளியீடு (Output)

#'user/all-numbers

மேலே உள்ள குறியீட்டில், நாம் (iterate inc 1) என்பதைப் பயன்படுத்தியுள்ளோம். அதாவது, iterate தனது முதல் ஆர்கியுமெண்டாக ஒரு செயல்பாட்டையும் (உதாரணமாக inc), இரண்டாவது ஆர்கியுமெண்டாக ஒரு ஆரம்ப மதிப்பையும் எடுத்துக்கொள்கிறது. இது all-numbers என்ற மாறியில் ஒதுக்கப்பட்டுள்ளது. இப்போது 25-வது உறுப்பை எடுக்கும்போது என்ன நடக்கிறது என்று பார்ப்போம்:

(nth all-numbers 24)

வெளியீடு (Output)

25

இந்த வெளியீடு, நாம் 1-க்கு இருபத்தி நான்கு முறை inc செயல்பாட்டைப் பயன்படுத்தியது போல் தோன்றுகிறது.

இப்போது முதல் 10 உறுப்புகளை எடுப்போம்:

(take 10 all-numbers)

வெளியீடு (Output)

(1 2 3 4 5 6 7 8 9 10)

முதல் உறுப்பான 1, iterate-க்கு வழங்கப்பட்ட ஆரம்ப மதிப்பு ஆகும். இரண்டாவது (inc 1), மூன்றாவது (inc (inc 1)) எனத் தொடர்கிறது. இது iterate ஆவணத்துடன் ஒத்துப்போகிறது: https://clojuredocs.org/clojure.core/iterate.

இப்போது ஒரு எண்ணை வர்க்கம் (square) செய்யும் செயல்பாட்டை உருவாக்குவோம்:

(defn squared [num]
  (Math/pow num 2))

வெளியீடு (Output)

#'user/squared

இப்போது 5-ன் வர்க்கங்களை iterate செய்வோம்:

(def squares (iterate squared 5))

வெளியீடு (Output)

#'user/squares

இப்போது முதல் மூன்று வர்க்கங்களை எடுப்போம்:

(take 3 squares)

வெளியீடு (Output)

(5 25.0 625.0)

இதற்கான விளக்கம் இதோ:

முதல் மதிப்பு 5, இது (def squares (iterate squared 5)) என்பதில் iterate-க்கு வழங்கப்பட்ட ஆரம்ப ஆர்கியுமெண்ட் ஆகும்; அது அப்படியே திருப்பிக் கொடுக்கப்படுகிறது.

இரண்டாவது மதிப்பு 25.0, இது முதல் இட்டரேஷனின் முடிவு: (squared 5).

மூன்றாவது மதிப்பு 625.0, இது இரண்டாவது இட்டரேஷனின் முடிவு: (squared (squared 5)).

16. map, filter, reduce, apply

16.1. map

இந்தப் பகுதிக்கான வீடியோவை இங்கே காணலாம் https://yu7.in/clj-map

உங்களிடம் ஒரு ஆர்கியுமெண்ட்டில் செயல்படும் ஒரு செயல்பாடு இருப்பதாக வைத்துக்கொள்வோம், உதாரணமாக inc (இங்கு (inc 1) என்பது 2-ஐத் தரும்). ஒரு கலெக்ஷன் அல்லது வரிசையில் உள்ள ஒவ்வொரு உறுப்பிற்கும் inc-ஐப் பயன்படுத்த, நீங்கள் map-ஐப் பயன்படுத்தலாம். கீழே காட்டியுள்ளபடி, முதலில் செயல்பாட்டை map-க்கு வழங்கவும், அதைத் தொடர்ந்து கலெக்ஷனை வழங்கவும்:

(map inc [1 2 3])

வெளியீடு (Output)

(2 3 4)

இந்த வழக்கில், map ஆனது [1 2 3]-ல் உள்ள ஒவ்வொரு உறுப்பிற்கும் inc செயல்பாட்டைப் பயன்படுத்துகிறது, முடிவுகளை ஒரு வரிசையாகச் சேகரித்து, அதைத் திருப்பிக் கொடுக்கிறது. இதன் விளைவாக (2 3 4) என்ற வெளியீடு கிடைக்கிறது.

கீழே உள்ள உதாரணத்தில், நான் (fn [x] (+ x 1)) என்ற பெயரற்ற செயல்பாட்டை map-க்கு வழங்கி, அதை [1 2 3]-க்குப் பயன்படுத்துகிறேன்:

(map (fn [x] (+ x 1)) [1 2 3])

வெளியீடு (Output)

(2 3 4)

முன்பே வரையறுக்கப்பட்ட செயல்பாடு தேவையில்லை என்பதை இது நிரூபிக்கிறது; Clojure-ல், நீங்கள் சிறிய செயல்பாடுகளை உடனுக்குடன் (on the fly) வரையறுக்க முடியும்.

பின்வரும் குறியீடு முந்தைய உதாரணங்களைப் போலவே செயல்படும்:

(map #(+ % 1) [1 2 3])

வெளியீடு (Output)

(2 3 4)

பெயரற்ற செயல்பாட்டை (fn [x] (+ x 1)) என்று எழுதுவதற்கு மற்றொரு வழி (+ % 1). செயல்பாட்டின் உடல் ()-க்குள் இருக்கும், மற்றும் % என்பது செயல்பாட்டிற்கு வழங்கப்படும் ஆர்கியுமெண்ட்டைக் குறிக்கிறது. இந்தச் சுருக்க வடிவம் (fn [x] (+ x 1)) என்பதை விட மிகவும் சுருக்கமானது.

இரண்டு ஆர்கியுமெண்டுகளை ஏற்கும் ஒரு செயல்பாட்டை எடுத்துக்கொள்வோம்:

(fn [x y]
  (+ (Math/pow x 2)
     (Math/pow y 2)))

இந்த செயல்பாடு இரண்டு ஆர்கியுமெண்டுகளை எடுத்து அவற்றின் வர்க்கங்களின் கூடுதலை (sum of their squares) வழங்குகிறது. இந்த செயல்பாட்டை வரிசைகளுடன் எவ்வாறு பயன்படுத்துவது? கீழே காட்டியுள்ளபடி இதை map-க்கு வழங்கவும்:

(map (fn [x y]
       (+ (Math/pow x 2)
          (Math/pow y 2))))

அடுத்து, முதல் ஆர்கியுமெண்ட்டிற்கான (x) வரிசையை வழங்கவும்:

(map (fn [x y]
       (+ (Math/pow x 2)
          (Math/pow y 2))) [1 2 3])

பின்னர், இரண்டாவது ஆர்கியுமெண்ட்டிற்கான (y) வரிசையை வழங்கவும்:

(map (fn [x y]
       (+ (Math/pow x 2)
          (Math/pow y 2))) [1 2 3] [2 3 4])

வெளியீடு (Output)

(5.0 13.0 25.0)

குறியீடு இவ்வாறு செயல்படுகிறது: map ஆனது [1 2 3]-ல் இருந்து முதல் உறுப்பையும் (அது 1), [2 3 4]-ல் இருந்து முதல் உறுப்பையும் (அது 2) எடுத்து இவற்றை பின்வரும் செயல்பாட்டிற்கு வழங்குகிறது:

(fn [x y]
  (+ (Math/pow x 2)
     (Math/pow y 2)))

செயல்பாடு வர்க்கங்களின் கூடுதலைக் கணக்கிட்டு 5.0-வை வழங்குகிறது. map இதை ஒரு வரிசையில் சேகரிக்கிறது, இதன் விளைவாக (5.0) கிடைக்கிறது.

பின்னர் map ஒவ்வொரு வரிசையிலிருந்தும் இரண்டாவது உறுப்பை (2 மற்றும் 3) எடுத்துக்கொள்கிறது, மற்றும் செயல்பாடு 13.0-வை வழங்குகிறது, அது முடிவு வரிசையுடன் சேர்க்கப்படுகிறது. மூன்றாவது உறுப்புகளுக்கும் இதே செயல்முறை மீண்டும் செய்யப்பட்டு, 25.0 வரிசையில் சேர்க்கப்படுகிறது. இறுதி முடிவாக (5.0 13.0 25.0) வழங்கப்படுகிறது.

கீழே உள்ள உதாரணத்தில், sum-of-squares என்ற செயல்பாட்டை வரையறுத்துள்ளேன்:

(defn sum-of-squares [x y]
  (+ (Math/pow x 2)
     (Math/pow y 2)))

இது முன்பு பயன்படுத்திய பெயரற்ற செயல்பாட்டிற்கு ஒரு பெயர் சூட்டுவது மட்டுமே:

(fn [x y]
  (+ (Math/pow x 2)
     (Math/pow y 2)))

நாம் sum-of-squares என்ற பெயரை map-உடன் பயன்படுத்தும்போதும், x மற்றும் y-க்கு இரண்டு வரிசைகளை வழங்கும்போதும் அதே முடிவைப் பெறலாம்:

(map sum-of-squares [1 2 3] [2 3 4])

வெளியீடு (Output)

(5.0 13.0 25.0)

பின்வரும் double-it என்ற செயல்பாடு, அதற்கு வழங்கப்படும் எண்ணை இரண்டால் பெருக்கி வழங்குகிறது:

(defn double-it [x]
  (* x 2))

வெளியீடு (Output)

#'user/double-it

ஒரு வெக்டரில் உள்ள எண்களை இரட்டிப்பாக்க இதை map-உடன் பயன்படுத்துவோம்:

(map double-it [1 2 3])

வெளியீடு (Output)

(2 4 6)

16.2. filter

இந்தப் பகுதிக்கான வீடியோவை இங்கே காணலாம் https://yu7.in/clj-filter

filter என்பது வழங்கப்பட்ட பிரடிகேட் செயல்பாடு (predicate function) true என்று திருப்பிக் கொடுக்கும் அசல் வரிசையின் உறுப்புகளை மட்டும் கொண்ட ஒரு வரிசையை வழங்குகிறது. உதாரணமாக, (odd? 1) என்பது true-ஐத் தரும், அதே சமயம் (odd? 2) என்பது false-ஐத் தரும். கீழே உள்ள குறியீட்டைப் பாருங்கள்:

(filter odd? (range 1 11))

வெளியீடு (Output)

(1 3 5 7 9)

இந்த உதாரணத்தில், நாம் odd?-ஐ முதல் ஆர்கியுமெண்டாகவும், 1 முதல் 10 வரையிலான எண்களின் வரிசையை (இது (range 1 11) மூலம் உருவாக்கப்பட்டது) இரண்டாவது ஆர்கியுமெண்டாகவும் வழங்குகிறோம். odd? எந்த எண்ணுக்கு true என்று வழங்குகிறதோ, அந்த எண்ணை filter எடுத்து முடிவில் சேர்க்கிறது.

அதேபோல், கீழே உள்ள உதாரணம் இரட்டை எண்களை (even numbers) மட்டும் வழங்குகிறது:

(filter even? (range 1 11))

வெளியீடு (Output)

(2 4 6 8 10)

கீழே உள்ள குறியீட்டில்:

(filter #(> % 5) (range 1 11))

வெளியீடு (Output)

(6 7 8 9 10)

இங்கே, நாம் #(> % 5) என்ற பெயரற்ற செயல்பாட்டை filter-க்கு வழங்குகிறோம். 1 முதல் 10 வரை உள்ள எண்களில் 5-ஐ விடப் பெரிய எண்கள் அனைத்தும் திருப்பிக் கொடுக்கப்படுகின்றன.

கீழே உள்ள உதாரணத்தில், 1 முதல் 10 வரை உள்ள எண்களில் 5-ஐ விடக் குறைவான எண்கள் அனைத்தும் திருப்பிக் கொடுக்கப்படுகின்றன:

(filter #(> 5 %) (range 1 11))

வெளியீடு (Output)

(1 2 3 4)
(filter (fn [x] (> x 5)) (range 1 11))

வெளியீடு (Output)

(6 7 8 9 10)

கீழே, நான் greater-than-5 என்ற செயல்பாட்டை வரையறுக்கிறேன், இது ஒரு எண் 5-ஐ விடப் பெரியதாக இருந்தால் true-ஐ வழங்குகிறது:

(defn greater-than-5 [x]
  (> x 5))

பின்னர் நான் அதை filter-உடன் பயன்படுத்தி 5-ஐ விடப் பெரிய எண்களைக் கண்டறிகிறேன்:

(filter greater-than-5 (range 1 11))

வெளியீடு (Output)

(6 7 8 9 10)

16.3. reduce

இந்தப் பகுதிக்கான வீடியோவை இங்கே காணலாம் https://yu7.in/clj-reduce

reduce ஒரு செயல்பாட்டை தற்போதைய அக்குமுலேட்டர் (accumulator) மற்றும் வரிசையின் அடுத்த உறுப்பு ஆகியவற்றிற்குத் திரும்பத் திரும்பப் பயன்படுத்துவதன் மூலம், ஒரு வரிசையை ஒற்றை மதிப்பாகக் குறைக்கிறது. கீழே உள்ள உதாரணத்தைப் பாருங்கள்:

(reduce + [1 2 3 4])

வெளியீடு (Output)

10

நாம் + செயல்பாட்டையும் [1 2 3 4] என்ற வரிசையையும் reduce-க்கு வழங்குகிறோம். reduce குறியீட்டை இவ்வாறு மாற்றி எழுதுவதாக நீங்கள் நினைக்கலாம்:

(+ 4 (+ 3 (+ 1 2)))

இது வரிசையை 10 என்ற ஒற்றை மதிப்பாகக் குறைக்கிறது.

reduce-ஐப் பயன்படுத்தி ஒரு வரிசையின் உறுப்புகளை எவ்வாறு பெருக்குவது என்று பார்ப்போம்:

(reduce * [1 2 3 4])

வெளியீடு (Output)

24

இரண்டு எண்களை ஏற்று அவற்றின் கூடுதலைத் திருப்பிக் கொடுக்கும் add என்ற செயல்பாட்டை வரையறுப்போம்:

(defn add [a b]
  (+ a b))

வெளியீடு (Output)

#'user/add

முன்பு +-ஐப் பயன்படுத்தியது போலவே, இதை நாம் reduce-உடன் பயன்படுத்தலாம்:

(reduce add [1 2 3 4])

வெளியீடு (Output)

10

அதேபோல், reduce குறியீட்டை இவ்வாறு மாற்றியமைக்கிறது:

(add 4 (add 3 (add 1 2)))

இப்போது, நான் multiply என்ற செயல்பாட்டை வரையறுக்கிறேன்:

(defn multiply [a b]
  (* a b))

வெளியீடு (Output)

#'user/multiply

அதை reduce-உடன் பயன்படுத்துகிறேன்:

(reduce multiply [1 2 3 4])

வெளியீடு (Output)

24

16.4. apply

இந்தப் பகுதிக்கான வீடியோவை இங்கே காணலாம்: https://yu7.in/clj-apply

+ செயல்பாடு பல எண்களை இவ்வாறு கூட்ட முடியும்:

(+ 1 2 3 4)

வெளியீடு (Output)

10

இருப்பினும், நீங்கள் ஒரு வரிசையை (sequence) ஒரே ஆர்கியுமெண்ட்டாக வழங்கினால், அந்தச் செயல்பாடு தோல்வியடையும்:

(+ [1 2 3 4]) ;; error

வெளியீடு (Output)

; Execution error (ClassCastException) at java.lang.Class/cast (Class.java:3921).
; Cannot cast clojure.lang.PersistentVector to java.lang.Number

இதைத் சரிசெய்ய, நாம் + செயல்பாட்டையும் வரிசையையும் apply-க்கு வழங்குகிறோம்:

(apply + [1 2 3 4])

வெளியீடு (Output)

10

இது வேலை செய்கிறது, ஏனெனில் apply வரிசையை தனித்தனி ஆர்கியுமெண்டுகளாக விரிவுபடுத்தி, குறியீட்டை இவ்வாறு மாற்றியமைக்கிறது:

(+ 1 2 3 4)

ஒரு வரிசை என்பது வெக்டர், பட்டியல் அல்லது மேப்பாக இருக்கலாம். இந்த உதாரணத்தில், நாம் எண்களின் பட்டியலை apply-உடன் பயன்படுத்துகிறோம்:

(apply + '(1 2 3 4))

வெளியீடு (Output)

10

இரண்டு ஆர்கியுமெண்டுகளை ஏற்று அவற்றை அச்சிடும் ஒரு செயல்பாட்டை எடுத்துக் கொள்வோம்:

(defn my-function [arg1 arg2]
  (println "Argument 1:" arg1)
  (println "Argument 2:" arg2))

வெளியீடு (Output)

#'user/my-function

இரண்டு ஆர்கியுமெண்டுகளை வழங்குவதன் மூலம் இந்த செயல்பாட்டை நாம் அழைக்கலாம்:

(my-function "one" "two")

வெளியீடு (Output)

Argument 1: one
Argument 2: two
nil

இரண்டு தனித்தனி ஆர்கியுமெண்டுகளுக்குப் பதிலாக, இரண்டு உறுப்புகளைக் கொண்ட ஒரு வெக்டரை நாம் வழங்கினால், செயல்பாட்டு அழைப்பு தோல்வியடையும்:

(my-function ["one" "two"]) ;; error

வெளியீடு (Output)

; Execution error (ArityException) at user/eval2061 (REPL:17).
; Wrong number of args (1) passed to: user/my-function

இதைத் தீர்க்க, நாம் செயல்பாட்டையும் வரிசையையும் apply-க்கு வழங்குகிறோம்:

(apply my-function ["one" "two"])

வெளியீடு (Output)

Argument 1: one
Argument 2: two
nil

17. டிஸ்ட்ரக்சரிங் (Destructuring)

இந்தப் பகுதிக்கான வீடியோவை இங்கே காணலாம்: https://yu7.in/clj-destructuring

Clojure தரவுகளைத் தொகுக்க லிஸ்ட்கள், வெக்டர்கள், மேப்கள் மற்றும் செட்கள் போன்ற பல்வேறு தரவு வகைகளை வழங்குகிறது. அவற்றைக் கொண்டு கணக்கீடுகளைச் செய்ய, நீங்கள் தரவை அன்-பேக் (unpack) செய்து, தேவையான மதிப்புகளை மாறிகளுக்கு ஒதுக்கி, அந்த மாறிகளை உங்கள் கணக்கீட்டில் பயன்படுத்த வேண்டும். இந்தப் பகுதி இதை எவ்வாறு செய்வது என்று விளக்குகிறது.

கீழே காட்டப்பட்டுள்ள vector_destructuring.clj நிரலைப் பாருங்கள்; அதைத் தட்டச்சு செய்து இயக்கவும்:

;; vector_destructuring.clj

(def people ["Rehmaan" "Kalaam"])

(let [[musician scientist] people]
  (println "Musician is" musician)
  (println "Scientist is" scientist))

வெளியீடு (Output)

Musician is Rehmaan
Scientist is Kalaam
nil

(def people ["Rehmaan" "Kalaam"]) என்ற வரியில், நாம் "Rehmaan" மற்றும் "Kalaam" ஆகியவற்றை ஒரு வெக்டரில் தொகுத்து அதற்கு people என்று பெயரிடுகிறோம். இப்போது இதைக் கவனியுங்கள்:

(let [[musician scientist] people]
  ;; other code goes here
 )

let வெளிப்பாட்டில், [[musician scientist] people] என்பதைப் பாருங்கள். musician மற்றும் scientist ஆகிய மாறிகள் people-ஐக் குறிக்கின்றன. people-ல் "Rehmaan" மற்றும் "Kalaam" உள்ளன என்பதை Clojure தீர்மானிக்கிறது, பின்னர் "Rehmaan"-ஐ musician-க்குள்ளும் "Kalaam"-ஐ scientist-க்குள்ளும் அன்-பேக் செய்கிறது. இறுதியாக, கீழே காட்டியுள்ளபடி println செயல்பாட்டைப் பயன்படுத்தி musician மற்றும் scientist-ஐ அச்சிடுகிறோம்:

(let [[musician scientist] people]
  (println "Musician is" musician)
  (println "Scientist is" scientist))

கீழே உள்ள vector_destructuring_2.clj-ன் குறியீட்டைப் பாருங்கள்; அதைத் தட்டச்சு செய்து இயக்கவும்.

;; vector_destructuring_2.clj

(def people ["Rehmaan" "Kalaam"])

(let [[musician scientist artist] people]
  (println "Musician is" musician)
  (println "Scientist is" scientist)
  (println "Artist is" artist))

வெளியீடு (Output)

Musician is Rehmaan
Scientist is Kalaam
Artist is nil
nil

மேலே உள்ள குறியீட்டில், people என்ற மாறி இரண்டு மதிப்புகளைக் கொண்ட ஒரு வெக்டருக்கு ["Rehmaan" "Kalaam"] ஒதுக்கப்பட்டுள்ளது. இருப்பினும், let பிளாக்கில், நாம் அதை மூன்று மாறிகளாக (musician, scientist, மற்றும் artist) டிஸ்ட்ரக்சரிங் செய்ய முயற்சி செய்கிறோம், அது கீழே காட்டப்பட்டுள்ளது:

(let [[musician scientist artist] people]
  ;; other stuff goes here
  )

இந்த வழக்கில், முதல் இரண்டு மாறிகளான musician மற்றும் scientist முறையே "Rehmaan" மற்றும் "Kalaam" ஆகிய மதிப்புகளைப் பெறுகின்றன, அதே சமயம் artist-க்கு nil ஒதுக்கப்படுகிறது. இதன் விளைவாக, அச்சிடும்போது artist என்பது nil என்று வருகிறது.

இப்போது கீழே காட்டப்பட்டுள்ள vector_destructuring_3.clj-ன் குறியீட்டைப் பாருங்கள்; அதைத் தட்டச்சு செய்து இயக்கவும்.

;; vector_destructuring_3.clj

(def people ["Rehmaan" "Kalaam" "Hussein" "Madhavan"])

(let [[musician scientist artist] people]
  (println "Musician is" musician)
  (println "Scientist is" scientist)
  (println "Artist is" artist))

வெளியீடு (Output)

Musician is Rehmaan
Scientist is Kalaam
Artist is Hussein
nil

மேலே உள்ள குறியீட்டில், people-ல் நான்கு மதிப்புகள் உள்ளன, ஆனால் அது மூன்று மாறிகளாக மட்டுமே டிஸ்ட்ரக்சரிங் செய்யப்படுகிறது:

(let [[musician scientist artist] people]
  ;; other code goes here
  )

இதன் விளைவாக, musician-க்கு "Rehmaan", scientist-க்கு "Kalaam", மற்றும் artist-க்கு "Hussein" வழங்கப்படுகிறது, அதே சமயம் people-ல் உள்ள இறுதி மதிப்பான "Madhavan" புறக்கணிக்கப்படுகிறது.

கீழே உள்ள குறியீட்டில்:

;; vector_destructuring_4.clj

(def people ["Rehmaan" "Kalaam" "Hussein" "Madhavan"])

(let [[musician scientist artist actor] people]
  (println "Musician is" musician)
  (println "Scientist is" scientist)
  (println "Artist is" artist)
  (println "Actor is" actor))

வெளியீடு (Output)

Musician is Rehmaan
Scientist is Kalaam
Artist is Hussein
Actor is Madhavan
nil

people-ல் உள்ள அனைத்து மதிப்புகளும் இப்போது musician, scientist, artist மற்றும் actor ஆகிய மாறிகளில் சரியாக அன்-பேக் செய்யப்பட்டுள்ளன.

பயன்படுத்தப்படாத மதிப்புகளுக்கு அண்டர்ஸ்கோர் பயன்படுத்துவது ஒரு மரபாகும். கீழே உள்ள குறியீட்டில், scientist மற்றும் actor மட்டுமே தேவைப்படுகின்றன; மற்ற பயன்படுத்தப்படாத மதிப்புகள் -ஆல் கைப்பற்றப்படுகின்றன.

;; vector_destructuring_5.clj

(def people ["Rehmaan" "Kalaam" "Hussein" "Madhavan"])

(let [[_ scientist _ actor] people]
  (println "Scientist is" scientist)
  (println "Actor is" actor))

வெளியீடு (Output)

Scientist is Kalaam
Actor is Madhavan
nil

இது குறியீட்டைப் படிப்பவர்களுக்கு இந்த மதிப்புகள் வேண்டுமென்றே புறக்கணிக்கப்பட்டுள்ளன என்பதை உணர்த்துகிறது.

மேலே உள்ள உதாரணங்கள் வெக்டர் டிஸ்ட்ரக்சரிங்கை விளக்குகின்றன, இதே தர்க்கம் லிஸ்ட்களுக்கும் பொருந்தும். வாசகர்கள் இதை முயற்சி செய்ய ஊக்குவிக்கப்படுகிறார்கள்.

Clojure மேப்களை டிஸ்ட்ரக்சரிங் செய்வதற்கான வழியையும் வழங்குகிறது. கீழே உள்ள map_destructuring.clj-ன் குறியீட்டைப் பாருங்கள்; அதைத் தட்டச்சு செய்து இயக்கவும்.

;; map_destructuring.clj

(def people
  {:musician "Rehmaan"
   :scientist "Kalaam"
   :artist "Hussein"
   :actor "Madhavan"})

(let [{scientist :scientist actor :actor} people]
  (println "Scientist is" scientist)
  (println "Actor is" actor))

வெளியீடு (Output)

Scientist is Kalaam
Actor is Madhavan
nil

நீங்கள் கவனித்தால்:

(def people
  {:musician "Rehmaan"
   :scientist "Kalaam"
   :artist "Hussein"
   :actor "Madhavan"})

உங்களிடம் நபர்களின் ஒரு மேப் உள்ளது, அதில் அவர்களின் தொழில் கீவேர்டாகவும் (keyword), பெயர் சரமாகவும் (string) உள்ளது. இந்த மேப்பை கீவேர்டுகளைப் பயன்படுத்தி டிஸ்ட்ரக்சரிங் செய்யலாம், அது கீழே காட்டப்பட்டுள்ளது:

(let [{scientist :scientist actor :actor} people]
    ;; other stuff here
  )

மேலே உள்ள குறியீட்டில், [{scientist :scientist actor :actor} people] என்ற வெளிப்பாட்டைக் கவனியுங்கள்; இங்கு, மேப் டிஸ்ட்ரக்சரிங் பேட்டர்ன் {scientist :scientist actor :actor} ஆனது people-க்கு பயன்படுத்தப்படுகிறது.

{scientist :scientist actor :actor} என்ற பேட்டர்ன் ஒரு மேப்பின் தலைகீழ் வடிவம் போலத் தோற்றமளிக்கிறது. :scientist என்ற கீவேர்டைத் தொடர்ந்து வரும் scientist என்ற மாறி, மேப்பில் :scientist-உடன் தொடர்புடைய மதிப்பை எடுத்து scientist என்ற மாறியில் ஒதுக்குகிறது. actor என்ற மாறியும் இதே முறையில் ஒதுக்கப்படுகிறது.

இது Clojure-ல் ஒரு மேப்பில் இருந்து தேவையான மதிப்புகளைத் தேர்ந்தெடுத்து எடுத்து மாறிகளுக்கு ஒதுக்குவதை மிகவும் எளிதாக்குகிறது.

18. த்ரெடிங் மேக்ரோக்கள் (Threading Macros)

நீங்கள் மற்ற நிரலாக்க மொழிகளில் இருந்து வந்திருந்தால், த்ரெடிங்கை (threading), த்ரெட்களுடன் (threads - குறியீட்டின் பகுதிகளை இணையாக இயக்குவது) குழப்பிக்கொள்ளாதீர்கள். Clojure-ல், த்ரெடிங் என்பது செயல்பாடுகளின் வரிசையை மிகவும் உள்ளுணர்வுடன் (intuitive) எழுதும் ஒரு வழியைக் குறிக்கிறது. இந்த அத்தியாயத்தைப் படிக்கும்போது இது உங்களுக்குத் தெளிவாகும்.

18.1. த்ரெட் ஃபர்ஸ்ட் (Thread First)

பின்வரும் நிரலைப் பார்த்து அதை இயக்கவும்:

(-> 5
    (Math/pow 2))

வெளியீடு (Output)

25.0

வெளியீடு 25.0 ஆகும். இதிலிருந்து, Clojure உங்கள் நிரலை இவ்வாறு மாற்றி எழுதுகிறது என்று நீங்கள் ஊகிக்கலாம்:

(Math/pow 5 2)

மேக்ரோ 'த்ரெட்-ஃபர்ஸ்ட்' (thread-first) என்று அழைக்கப்படுகிறது. இதன் பொருள் நீங்கள் இவ்வாறு எழுதினால்:

(-> 5)

அதைத் தொடர்ந்து Math/pow போன்ற ஒரு செயல்பாட்டை வழங்கினால்:

(-> 5
    (Math/pow 2))

த்ரெட்-ஃபர்ஸ்ட் மேக்ரோ 5-ஐ Math/pow செயல்பாட்டின் முதல் ஆர்கியுமெண்டாக வைக்கும். இது பின்வருபதற்குச் சமமாகும்:

(Math/pow 5 2)

இப்போது கீழே உள்ள குறியீட்டைப் பாருங்கள், இங்கு 5 முதலில் (Math/pow 2)-க்கு முதல் ஆர்கியுமெண்டாக வழங்கப்பட்டு, பின்னர் inc-க்கு வழங்கப்படுகிறது. நிரலை இயக்கவும்:

(-> 5
    (Math/pow 2)
    inc)

வெளியீடு (Output)

26.0

இது 26.0-வை வெளியீடாக வழங்குகிறது. நிரலின் முதல் படி இவ்வாறு மாற்றி எழுதப்படலாம்:

(-> (Math/pow 5 2)
    inc)

இப்போது, (Math/pow 5 2)-ன் முடிவு inc-க்கு முதல் ஆர்கியுமெண்டாக வழங்கப்படுகிறது, மேலும் குறியீடு இவ்வாறு மாறுகிறது:

(inc (Math/pow 5 2))

எனவே இது 26.0 என்ற வெளியீட்டைத் தருகிறது.

இந்த நிரலைப் பார்த்து அதை இயக்கவும்:

(-> 10
    inc
    (* 2)
    (+ 5))

வெளியீடு (Output)

27

இதை இவ்வாறு மாற்றி எழுதலாம்:

(+ (* (inc 10) 2) 5)

இதை இயக்கினால் 27 கிடைக்கும்.

18.2. த்ரெட் லாஸ்ட் (Thread Last)

த்ரெட்-ஃபர்ஸ்ட், த்ரெட்டட் மதிப்பை ஒரு செயல்பாட்டின் முதல் ஆர்கியுமெண்டாக வைக்கும் அதே வேளையில், த்ரெட்-லாஸ்ட் (→>) அதை கடைசி ஆர்கியுமெண்டாக வைக்கும். ஒரு உதாரணத்தைப் பார்ப்போம்.

கீழே உள்ள குறியீட்டை இயக்கவும்:

(->> 5
    (Math/pow 2))

வெளியீடு (Output)

32.0

5 என்ற மதிப்பு த்ரெட்-லாஸ்ட் மேக்ரோ →>-க்கு வழங்கப்பட்டுள்ளதால், மேலே உள்ள குறியீடு பின்வருபதற்குச் சமமாகும்:

(Math/pow 2 5)

வேறு வார்த்தைகளில் கூறுமில் என்றால், 5 ஆனது (Math/pow 2)-ன் இறுதியில் வைக்கப்படுகிறது, இதன் விளைவாக 32.0 கிடைக்கிறது.

பின்வரும் உதாரணத்தைக் கவனியுங்கள்:

(->> 5
     (Math/pow 2)
     inc)

வெளியீடு (Output)

33.0

இதை இவ்வாறு மாற்றி எழுதலாம்:

(inc (Math/pow 2 5))

இது 33.0 என மதிப்பீடு செய்யப்படுகிறது.

18.3. த்ரெட் ஆஸ் (Thread as)

சில நேரங்களில், ஒரு செயல்பாட்டிற்குள் த்ரெட்டட் மதிப்பை ஏதேனும் ஒரு இடத்தில் (arbitrary position) வழங்க வேண்டியிருக்கும். இத்தகைய சூழ்நிலைகளில், நீங்கள் as→ (thread-as) மேக்ரோவைப் பயன்படுத்தலாம்.

கீழே உள்ள குறியீட்டில்:

(as-> 5 x
  (Math/pow x 2))

வெளியீடு (Output)

25.0

த்ரெட்டட் மதிப்பு x என்ற குறியீட்டுடன் (symbol) இணைக்கப்பட்டுள்ளது, இது x-ஐ வெளிப்பாட்டின் எந்த இடத்திலும் வைக்க அனுமதிக்கிறது. முந்தைய உதாரணத்தில், நாம் அதை (Math/pow x 2) என்று வைத்தோம், இது \$x^2\$-ஐக் கணக்கிடுகிறது.

பின்வரும் உதாரணத்தில்:

(as-> 5 x
  (Math/pow 2 x))

வெளியீடு (Output)

32.0

நாம் x-ஐ Math/pow-ன் கடைசி ஆர்கியுமெண்டாக வழங்குகிறோம், இது \$2^x\$-ஐக் கணக்கிடுகிறது.

18.4. நிபந்தனை த்ரெடிங் (Conditional Threading)

நீங்கள் நிபந்தனையின் அடிப்படையில் மதிப்புகளை த்ரெட் செய்ய விரும்பினால், நிபந்தனை த்ரெடிங்கை (conditional threading) பயன்படுத்தலாம். பின்வரும் உதாரணத்தைக் கவனியுங்கள்:

(let [a 11]
  (cond-> []
   (odd? a) (conj (* a 2))
   (even? a) (conj (/ a 2))))

வெளியீடு (Output)

[22]

cond→ மேக்ரோ என்பது நிபந்தனை த்ரெட்-ஃபர்ஸ்ட் ஆபரேட்டர் ஆகும். இந்த வழக்கில், நாம் ஒரு காலியான வெக்டரை [] அதற்கு வழங்குகிறோம்:

(let [a 11]
  (cond-> []
   ....))

cond→ []-ஐத் தொடர்ந்து நிபந்தனைகள் மற்றும் வெளிப்பாடுகளின் ஜோடிகள் உள்ளன. நிபந்தனை உண்மையாக இருந்தால் மட்டுமே வெளிப்பாடு இயக்கப்படும். மேலே உள்ள குறியீட்டை இவ்வாறு மாற்றி எழுதலாம்:

(let [a 11]
  (cond
   (odd? a) (conj [] (* a 2))
   (even? a) (conj [] (/ a 2))))

இந்த உதாரணத்தில், a ஒரு ஒற்றைப்படை எண், எனவே (conj [] (* a 2)) என்ற வெளிப்பாடு இயக்கப்பட்டு, [22] என்ற முடிவு கிடைக்கிறது.

பின்வரும் உதாரணத்தில் ஒரு சிறிய வேறுபாடு உள்ளது: நிபந்தனை த்ரெட்-ஃபர்ஸ்ட் மேக்ரோ cond→-க்கு பதிலாக, நாம் நிபந்தனை த்ரெட்-லாஸ்ட் மேக்ரோ cond→>-ஐப் பயன்படுத்துகிறோம்.

பின்வரும் குறியீட்டை இயக்கவும்:

(let [a 10]
  (cond->> 1
   (odd? a) (* a 2)
   (even? a) (/ a 2)))

வெளியீடு (Output)

5

மேலே உள்ள குறியீடு பின்வருபதற்குச் சமமாகும்:

(let [a 10]
  (cond
   (odd? a) (* a 2 1)
   (even? a) (/ a 2 1)))

a ஒரு இரட்டைப்படை எண் என்பதால், (/ a 2 1) என்ற வெளிப்பாடு இயக்கப்படுகிறது. கணித ரீதியாக, இது $10 \div 2 \div 1$ ஆகும், இதன் முடிவு 5.

19. ரெகுலர் எக்ஸ்பிரஷன் (Regular Expression)

இதற்கான மூலக் குறியீட்டை (source code) இங்கே பெறலாம் https://gitlab.com/clojure-book/code/-/raw/master/regular_expression.cl
இந்தப் பகுதிக்கான வீடியோவை இங்கே காணலாம் https://yu7.in/clj-regexp

சரம் (string) என்றால் என்ன என்பது நமக்குத் தெரியும்; இது " மற்றும் " (இரட்டை மேற்கோள் குறிகளுக்குள்) அடைக்கப்பட்ட எழுத்துக்களின் தொகுப்பாகும். கீழே ஒரு சரத்தைக் காணலாம்:

"A string"

வெளியீடு (Output)

"A string"

அதன் வகையை நாம் பார்க்கும்போது, அது ஒரு சரம் என்று நமக்குத் தெரிவிக்கிறது:

(type "A string")

வெளியீடு (Output)

java.lang.String

இப்போது சில எழுத்துக்களை " மற்றும் "-க்கு இடையில் வைத்தால் என்ன நடக்கும்? கீழே நான் regexp என்பதை " மற்றும் "-க்கு இடையில் வைத்துள்ளேன்:

#"regexp"

வெளியீடு (Output)

#"regexp"

அதன் வகையைப் பற்றி நான் வினவினால், அது ஒரு java.util.regex.Pattern என்று எனக்குத் தெரிகிறது; அதாவது, இது ஒரு சரம் அல்ல, இது ஒரு வடிவம் (pattern).

(type #"regexp")

வெளியீடு (Output)

java.util.regex.Pattern

ஒரு சரத்தில் குறிப்பிட்ட வடிவங்கள் (patterns) உள்ளனவா என்பதைக் கண்டறிய ரெகுலர் எக்ஸ்பிரஷன்களைப் பயன்படுத்தலாம். உதாரணமாக, ஒரு மின்னஞ்சல் முகவரிக்கு ஒரு குறிப்பிட்ட வடிவம் உள்ளது. எனது மின்னஞ்சல் mindaslab@protonmail.com என்று வைத்துக்கொள்வோம்: mindaslab என்பது எனது பயனர் பெயர் (username), அதைத் தொடர்ந்து ஒரு @ குறியீடு உள்ளது, com என்பது ஒரு டாப்-லெவல் டொமைன், மற்றும் protonmail என்பது com-ன் ஒரு சப்-டொமைன். சப்-டொமைனும் டாப்-லெவல் டொமைனும் ஒரு புள்ளியால் (dot) பிரிக்கப்பட்டுள்ளன. நீங்கள் பல மின்னஞ்சல் முகவரிகளைப் பார்க்கும்போது, <username>@<subdomain>.<top level domain> என்பது ஒரு மின்னஞ்சலின் ரெகுலர் எக்ஸ்பிரஷன் என்று நீங்கள் நினைக்கலாம்.

அதேபோல், நீங்கள் பல அஞ்சல் முகவரிகளைப் பார்த்தால், வீட்டு எண்ணும் தெருப் பெயரும் மேலேயும், பின்கோடு அல்லது ஜிப் கோடு கீழேயும் இருப்பதை நீங்கள் கவனிக்கலாம்.

உங்களுக்கு ஒரு மின்னஞ்சல் முகவரி அல்லது அஞ்சல் முகவரி வழங்கப்படும் போது, நீங்கள் எதைப் பார்க்கிறீர்கள் என்பதை அடையாளம் காண இந்த ரெகுலர் எக்ஸ்பிரஷன்கள் உதவுகின்றன.

இவற்றை நொடிப் பொழுதில் அடையாளம் காண உங்கள் மூளைக்கு மில்லியன் கணக்கான ஆண்டுகளின் பரிணாம வளர்ச்சியும் நிறைய அனுபவமும் உள்ளது. ஆனால் கணினிகளுக்கு, ஒரு சரத்தில் வடிவங்களை எவ்வாறு ஸ்கேன் செய்ய வேண்டும் மற்றும் அவை என்ன என்பதை எவ்வாறு அடையாளம் காண வேண்டும் என்பதை நாம் புரோகிராமிங் மூலம் குறிப்பிட வேண்டும்.

இப்போது ஒரே ஒரு இலக்கத்தை (digit) மட்டும் அடையாளம் காண ஒரு ரெகுலர் எக்ஸ்பிரஷனை எழுதுவோம். அது "\d". இங்கு " என்பது ரெகுலர் எக்ஸ்பிரஷனின் தொடக்கத்தையும் " அதன் முடிவையும் குறிக்கிறது. \d என்பது நாம் ஒரே ஒரு இலக்கத்தைத் தேடுகிறோம் என்று பொருள். இங்கே, \ என்பது ஒரு எஸ்கேப் சீக்வென்ஸ் (escape sequence) ஆகும், இது #"\d" என்பதை எழுத்து d-க்கு பதிலாக ஒரு இலக்கத்தைத் தேடச் செய்கிறது.

ஒரு சரம் ரெகுலர் எக்ஸ்பிரஷனுடன் பொருந்துகிறதா என்று பார்க்க re-matches என்ற செயல்பாட்டைப் பயன்படுத்தலாம். "There is no number here." என்பது #"\d" உடன் பொருந்துகிறதா என்று பார்ப்போம்:

(re-matches #"\d" "There is no number here.")

வெளியீடு (Output)

nil

சரத்தில் எந்த எண்ணும் இல்லாததால், இது பொருந்தவில்லை.

இப்போது #"\d" ஆனது "4" உடன் பொருந்துகிறதா என்று பார்ப்போம்:

(re-matches #"\d" "4")

வெளியீடு (Output)

"4"

இது பொருந்துகிறது, ஏனெனில் "4" ஒரே ஒரு இலக்கத்தைக் கொண்டுள்ளது மற்றும் #"\d" தேடுவது அதையேதான்.

இப்போது #"\d" ஆனது "42" உடன் பொருந்துகிறதா என்று பார்ப்போம்:

(re-matches #"\d" "42")

வெளியீடு (Output)

nil

இது பொருந்தாது, ஏனெனில் "42" ஒன்றுக்கு மேற்பட்ட இலக்கங்களைக் கொண்டுள்ளது.

ரெகுலர் எக்ஸ்பிரஷனில் "ஒன்று அல்லது அதற்கு மேற்பட்ட" (one or more) என்பதைக் குறிப்பிட, நாம் + குறியீட்டைப் பயன்படுத்துகிறோம், எனவே #"\d+" என்பது ஒன்று அல்லது அதற்கு மேற்பட்ட இலக்கங்களைத் தேடும் என்று பொருள். கீழே உள்ள உதாரணத்தைப் பாருங்கள்:

(re-matches #"\d+" "42")

வெளியீடு (Output)

"42"

இது பொருந்துகிறது, ஏனெனில் "42" ஒன்று அல்லது அதற்கு மேற்பட்ட இலக்கங்களைக் கொண்டுள்ளது.

#"\d+" ஆனது "42 is the ultimate answer." உடன் பொருந்தாது என்பது ஆச்சரியமாக இருக்கலாம், அதை கீழே காண்போம்:

(re-matches #"\d+" "42 is the ultimate answer.")

வெளியீடு (Output)

nil

ஏனெனில் re-matches ஒரு சரம் ரெகுலர் எக்ஸ்பிரஷனுடன் சரியாக (exactly) பொருந்துகிறதா என்று சரிபார்க்கிறது. "42 is the ultimate answer." என்பதில் 42-ஐத் தொடர்ந்து ஒரு இடைவெளியும் மற்ற வார்த்தைகளும் உள்ளன, இது #"\d+"-க்கு சரியான பொருத்தம் அல்ல, எனவே இது nil-ஐ வழங்குகிறது.

ஒரு சரத்தில் உள்ள வடிவத்தைக் கண்டறிய, நாம் re-find-ஐப் பயன்படுத்தலாம்:

(re-find #"\d+" "42 is the ultimate answer.")

வெளியீடு (Output)

"42"

இது "42"-ஐ வழங்குகிறது, எனவே ஒரு சரத்திலிருந்து ஒரு வடிவத்தை இது மீட்க முடியும் என்பதை நாம் புரிந்துகொள்ளலாம்.

இப்போது 2-இலக்க எண்களைத் தேடுவோம்:

(re-find #"\d+" "42 is the ultimate answer, and so is 52.")

வெளியீடு (Output)

"42"

இது "42" மற்றும் "52" ஆகிய இரண்டையுமே കണ്ടെ சொல்லும் என்று நீங்கள் நினைத்திருந்தால், நீங்கள் ஏமாற்றமடைவீர்கள், ஏனெனில் மேலே "42" மட்டுமே உள்ளது. re-find முதல் பொருத்தத்தை (first match) மட்டுமே வழங்குகிறது:

(re-find #"\d+" "Forty two is the ultimate answer, and so is 52.")

வெளியீடு (Output)

"52"

மேலே உள்ள உதாரணத்தில் பார்த்தது போல, re-find முதல் பொருத்தமான "52"-வை மட்டுமே வழங்குகிறது.

அனைத்து பொருத்தங்களையும் பெற, re-seq-ஐப் பயன்படுத்தவும்:

(re-seq #"\d+" "42 is the ultimate answer, and so is 52.")

வெளியீடு (Output)

("42" "52")

இது ரெகுலர் எக்ஸ்பிரஷனுடன் பொருந்தும் அனைத்து சரங்களையும் கொண்ட ஒரு வரிசையை வழங்குகிறது; நமது வழக்கில், அது #"\d+".

19.1. கேஸ் சென்சிட்டிவ் மற்றும் இன்சென்சிட்டிவ் பொருத்தங்கள் (Case sensitive and insensitive matches)

இப்போது நம்மிடம் #"abc" என்ற ரெகுலர் எக்ஸ்பிரஷன் இருப்பதாக வைத்துக்கொள்வோம், இது ஒரு சரத்தில் abc என்ற எழுத்துக்களைப் பொருத்த உதவும், ஆனால் ABC-யை அல்ல, ஏனெனில் ABC பெரிய எழுத்துக்கள். ரெகுலர் எக்ஸ்பிரஷனை கேஸ்-இன்சென்சிட்டிவ் (case-insensitive) ஆக்குவதற்கு ஒரு வழி உள்ளது. ரெகுலர் எக்ஸ்பிரஷனுடன் (?i) என்பதைச் சேர்ப்பதன் மூலம் நீங்கள் Clojure-க்கு அதை கேஸ்-இன்சென்சிட்டிவ் ஆக்கச் சொல்லலாம்:

(re-seq #"(?i)abc" "abc are small letters and ABC are capitals.")

வெளியீடு (Output)

("abc" "ABC")

எனவே #"(?i)abc" என்பது "abc", "ABC", "AbC" போன்ற அனைத்தையும் பொருந்தும்.

கீழே #"abc" ஆனது "ABC" உடன் எவ்வாறு பொருந்தவில்லை என்பதை நீங்கள் காணலாம்:

(re-seq #"abc" "abc are small letters and ABC are capitals.")

வெளியீடு (Output)

("abc")

கீழே உள்ள உதாரணத்தில் #"(?i)ABC" ஆனது "abc", "ABC" ஆகியவற்றுடன் பொருந்துவதை நீங்கள் காணலாம், ஏனெனில் இது கேஸ்-இன்சென்சிட்டிவ்:

(re-seq #"(?i)ABC" "abc are small letters and ABC are capitals.")

வெளியீடு (Output)

("abc" "ABC")

மற்றொரு கேஸ்-இன்சென்சிட்டிவ் ரெகுலர் எக்ஸ்பிரஷன் #"(?i)abC" செயல்பாட்டில்:

(re-seq #"(?i)abC" "abc are small letters and ABC are capitals.")

வெளியீடு (Output)

("abc" "ABC")

19.2. எழுத்துக்களின் வரம்புகளை ஸ்கேன் செய்தல் (Scanning for range of characters)

இப்போது வரம்புகளை (ranges) சரிபார்ப்போம். நீங்கள் பெரிய எழுத்துக்களை ஸ்கேன் செய்ய விரும்புகிறீர்கள் என்று வைத்துக்கொள்வோம்—குறிப்பாக, ஒன்று அல்லது அதற்கு மேற்பட்டவற்றை. அதற்கு, நீங்கள் + குவாண்டிஃபையரைப் பயன்படுத்த வேண்டும்.

பெரிய எழுத்துக்கள் A முதல் Z வரை இருக்கும், எனவே ஒரு ரெகுலர் எக்ஸ்பிரஷனில், நீங்கள் அதை #"[A-Z]+" என்று எழுதலாம். இதன் பொருள் "A முதல் Z வரையிலான வரம்பில் ஒன்று அல்லது அதற்கு மேற்பட்ட பெரிய எழுத்துக்களை ஸ்கேன் செய்" என்பதாகும். ஒரு உதாரணத்தைச் செய்து பார்ப்போம்:

(re-seq #"[A-Z]+" "Finds all CAPITAL letter WORDS.")

வெளியீடு (Output)

("F" "CAPITAL" "WORDS")

இது வேலை செய்கிறது என்று தோன்றுகிறது.

19.3. ஸ்ட்ரிங் செயல்பாடுகளுடன் பயன்படுத்துதல் (Using with string functions)

Clojure-ல் சில ஸ்ட்ரிங் செயல்பாடுகளும் ரெகுலர் எக்ஸ்பிரஷன்களை ஏற்கின்றன, அவற்றை செயல்பாட்டில் பார்ப்போம்.

முதலில், Clojure ஸ்ட்ரிங் லைப்ரரியை str என அழைக்கிறோம்:

(require '[clojure.string :as str])

கீழே உள்ள குறியீட்டில், "There are lots of spaces" என்பதில் நிறைய இடைவெளிகள் உள்ளன. நாம் #"\s+" என்ற ரெகுலர் எக்ஸ்பிரஷனை உருவாக்குகிறோம், இதன் பொருள் "ஒன்று அல்லது அதற்கு மேற்பட்ட இடைவெளி எழுத்துக்கள்" (whitespace characters). பின்னர் நாம் replace செயல்பாட்டிற்கு ஒன்று அல்லது அதற்கு மேற்பட்ட இடைவெளிகளை ஒரு ஒற்றை இடைவெளி " "-ஆல் மாற்றச் சொல்கிறோம்:

(str/replace "There    are     lots of   spaces" #"\s+" " ")

வெளியீடு (Output)

"There are lots of spaces"

இது வேலை செய்கிறது.

கீழே உள்ள குறியீட்டில் நாம் ஒரு ஸ்பிளிட் (split) செயல்பாட்டைப் பயன்படுத்துகிறோம்:

(str/split "There   are     lots of   spaces" #"\s")

வெளியீடு (Output)

["There" "" "" "are" "" "" "" "" "lots" "of" "" "" "spaces"]

நாம் "There are lots of spaces" என்பதை ஒரு ஒற்றை இடைவெளி #"\s" மூலம் பிரிக்கச் சொல்கிறோம், அதனால் மேலே உள்ள முடிவு கிடைக்கிறது, இது நாம் எதிர்பார்த்த முடிவு இல்லை என்று நினைக்கிறேன். நாம் செய்ய வேண்டியது, ஒன்று அல்லது அதற்கு மேற்பட்ட இடைவெளிகளைக் கொண்டு பிரிக்கச் சொல்லுவது, அது கீழே காட்டப்பட்டுள்ளது:

(str/split "There   are     lots of   spaces" #"\s+")

வெளியீடு (Output)

["There" "are" "lots" "of" "spaces"]

இப்போது வார்த்தைகள் சரியாகப் பிரிக்கப்பட்டு ஒரு நல்ல முடிவு கிடைக்கிறது.

19.4. நினைவில் கொள்ள வேண்டியவை

ரெகுலர் எக்ஸ்பிரஷன்களைப் பயன்படுத்த விரும்பும் போது நீங்கள் நினைவில் கொள்ள வேண்டிய அல்லது அவ்வப்போது சரிபார்க்க வேண்டிய சில விஷயங்கள் உள்ளன. அவை கீழே உள்ள அட்டவணையில் குறிப்பிடப்பட்டுள்ளன [8].

விஷயம்

பொருள்

.

ஏதேனும் ஒரு ஒற்றை எழுத்து

\w

ஏதேனும் ஒரு வார்த்தை எழுத்து (எழுத்து, எண், அண்டர்ஸ்கோர்)

\W

வார்த்தை எழுத்து அல்லாத ஏதேனும் ஒரு எழுத்து

\d

ஏதேனும் ஒரு இலக்கம்

\D

இலக்கம் அல்லாத ஏதேனும் ஒரு எழுத்து

\s

ஏதேனும் ஒரு இடைவெளி எழுத்து (whitespace character)

\S

இடைவெளி எழுத்து அல்லாத ஏதேனும் ஒரு எழுத்து

\b

ஏதேனும் ஒரு வார்த்தை எல்லைக் குறியீடு (word boundary character)

^

வரிகளின் தொடக்கம்

$

வரிகளின் முடிவு

\A

சரத்தின் தொடக்கம்

\z

சரத்தின் முடிவு

[abc]

a, b அல்லது c ஆகியவற்றில் ஏதேனும் ஒரு ஒற்றை எழுத்து

[^abc]

a, b, அல்லது c தவிர மற்ற ஏதேனும் ஒரு ஒற்றை எழுத்து

[a-z]

a-z வரம்பில் ஏதேனும் ஒரு ஒற்றை எழுத்து

[a-zA-Z]

a-z அல்லது A-Z வரம்பில் ஏதேனும் ஒரு ஒற்றை எழுத்து

(…​)

உள்ளே இருப்ப everything-ஐயும் கைப்பற்றுதல்

(a|b)

a அல்லது b

a?

பூஜ்ஜியம் அல்லது ஒரு முறை a

a*

பூஜ்ஜியம் அல்லது அதற்கு மேற்பட்ட முறை a

a+

ஒன்று அல்லது அதற்கு மேற்பட்ட முறை a

a{3}

சரியாக 3 முறை a

a{3,}

3 அல்லது அதற்கு மேற்பட்ட முறை a

a{3,6}

3 மற்றும் 6 முறைக்கு இடைப்பட்ட a

i

கேஸ்-இன்சென்சிட்டிவ் (case-insensitive)

m

புள்ளி (dot) புதிய வரிகளையும் (newlines) பொருத்துகிறது

x

ரெஜெக்ஸில் உள்ள இடைவெளிகளைப் புறக்கணிக்கிறது

o

#{…​} மாற்றங்களை ஒரு முறை மட்டுமே செய்கிறது

உங்களுக்கு இது உடனடியாகப் புரியவில்லை என்றால் கவலைப்பட வேண்டாம், நீங்கள் படித்துக் கொள்ளும்போது புரிந்துகொள்வீர்கள்.

19.5. இன்னும் நிறைய விடுபட்டுள்ளன

ரெகுலர் எக்ஸ்பிரஷன்கள் ஒரு மிகப்பெரிய தலைப்பு, இந்தப் புத்தகம் ஒரு அறிமுகத்தை மட்டுமே வழங்குகிறது. இது ரெகுலர் எக்ஸ்பிரஷன்களின் கருத்தை அறிமுகப்படுத்துகிறது, அவ்வளவுதான். வரும் வெளியீடுகளில் நான் இன்னும் கூடுதல் உள்ளடக்கத்தைச் சேர்க்க முயற்சிப்பேன். மேலும் அறிய, நீங்கள் Mastering Regular Expressions (https://amzn.to/3YvByJM) மற்றும் Regular Expression Pocket Reference (https://amzn.to/3DNXvtQ) ஆகியவற்றை அணுகலாம்.

20. பெரிய நிரல்களைப் பிரித்தல் (Splitting Large Programs)

இந்தப் பகுதிக்கான வீடியோவை இங்கே காணலாம் https://yu7.in/clj-slp

மிகச்சிறிய, ஒரே வரி கொண்ட Clojure ஸ்கிரிப்ட்டை Clojure REPL-ல் எழுதலாம், மற்றும் பெரிய ஸ்கிரிப்ட்டை ஒரு கோப்பில் எழுதலாம். ஆனால் ஒரு ஸ்கிரிப்ட் ஒரே கோப்பிற்கு மிகவும் பெரியதாகி, நிர்வகிப்பது கடினமாக மாறினால் என்ன நடக்கும்? நீங்கள் அதை பல கோப்புகளாகப் பிரிக்கலாம். உங்கள் Clojure குறியீட்டில் ஒரு கோப்பிலிருந்து மற்றொரு கோப்பிற்கு ஸ்கிரிப்ட்களை எவ்வாறு ஏற்றுவது என்பதை இந்தப் பகுதி விளக்குகிறது.

உதாரணமாக, கீழே உள்ளதைப் போன்ற ஒரு நிரல் உங்களிடம் இருப்பதாக வைத்துக்கொள்வோம்:

;; calculator.clj

(defn add [a b]
  (+ a b))

(defn sub [a b]
  (- a b))

(def a 5)
(def b 3)

(println a " + " b " = " (add a b))
(println a " - " b " = " (sub a b))

வெளியீடு (Output)

5  +  3  =  8
5  -  3  =  2

இது சரியாக வேலை செய்கிறது, ஆனால் ஒரு கோப்பு இரண்டு வெவ்வேறு விஷயங்களைச் செய்வதை நீங்கள் கவனிப்பீர்கள். முதலில், இது add மற்றும் sub என்ற கணக்கீட்டு செயல்பாடுகளை வரையறுக்கிறது:

(defn add [a b]
  (+ a b))

(defn sub [a b]
  (- a b))

பின்னர், அது ஒரு முடிவைக் கணக்கிட அந்த செயல்பாடுகளைப் பயன்படுத்துகிறது:

(def a 5)
(def b 3)

(println a " + " b " = " (add a b))
(println a " - " b " = " (sub a b))

இதை இரண்டு வெவ்வேறு கோப்புகளாகப் பிரிப்பது சிறப்பாக இருக்குமல்லவா? செயல்பாட்டு வரையறைகளை calc_lib.clj என்ற கோப்பிற்கு மாற்றலாம்:

;; calc_lib.clj

(defn add [a b]
  (+ a b))

(defn sub [a b]
  (- a b))

பின்னர், calc_ui.clj என்ற மற்றொரு கோப்பில், பின்வரும் குறியீட்டை எழுதுகிறோம்:

;; calc_ui.clj

(load-file "calc_lib.clj")

(def a 5)
(def b 3)

(println a " + " b " = " (add a b))
(println a " - " b " = " (sub a b))

(load-file "calc_lib.clj") என்ற வரியைக் கவனியுங்கள்; இது calc_lib.clj கோப்பை calc_ui.clj-க்குள் ஏற்றுகிறது. இப்போது நாம் 5 மற்றும் 3-ஐக் கூட்டுவதற்கும் கழிப்பதற்கும் முடிவைக் கணக்கிடலாம்.

கணக்கீட்டு செயல்பாடுகளைத் தனி கோப்பில் பிரிப்பதன் மூலம், கூட்டல் அல்லது கழித்தல்களைச் செய்ய வேண்டிய வேறு எந்தக் கோப்பிலும் அவற்றை நீங்கள் இணைக்க முடியும். இது மறுபயன்பாட்டினை (reusability) கணிசமாக அதிகரிக்கிறது.

21. ரெக்கார்ட்ஸ் மற்றும் புரோட்டோகால்கள் (Records and Protocols)

ஆப்ஜெக்ட்-ஓரியண்டட் புரோகிராமிங் (OOP) உச்சத்தில் இருந்த ஒரு காலத்தில் Clojure உருவானது. காலப்போக்கில், குறியீட்டுத் தொகுப்புகள் (codebases) பெருகி சிக்கலானதாக மாறியபோது, OOP-ன் சில குறைபாடுகள் வெளிப்பட்டன, இது பலரை ஃபங்க்ஷனல் புரோகிராமிங்கை (functional programming) ஆராய வழிவகுத்தது. எனவே, Clojure ஆப்ஜெக்ட்களை உருவாக்கவும் அவற்றோடு தொடர்பு கொள்ளவும் வழிகளை வழங்குகிறது என்பதில் ஆச்சரியமில்லை.

Clojure-ல், ஒரு ஆப்ஜெக்ட் என்பது அடிப்படையில் ஒரு மேம்படுத்தப்பட்ட மேப் (map) — அதற்கு மேல் ஒன்றுமில்லை. உங்களுக்கு அவை எப்போதும் தேவைப்படாமல் இருக்கலாம், ஆனால் உங்கள் பயன்பாட்டிற்கு அவை கிடைக்கின்றன.

21.1. ஆப்ஜெக்ட்களை உருவாக்குதல் (Creating Objects)

இந்தப் பகுதிக்கு, நீங்கள் https://gitlab.com/clojure-book/code/-/raw/master/record.clj-ல் உள்ள குறியீட்டைப் பயன்படுத்தலாம். உங்கள் REPL டெர்மினலில் இயங்கினாலும் அல்லது உங்கள் IDE-உடன் ஒருங்கிணைக்கப்பட்டிருந்தாலும், வரிகளை ஒவ்வொன்றாக நகலெடுத்து (copy) பேஸ்ட் (paste) செய்யவும்.
இந்தப் பகுதிக்கான வீடியோவை இங்கே காணலாம்: https://yu7.in/clj-records

Clojure-ல் ஆப்ஜெக்ட்கள் ரெக்கார்ட்ஸ் (Records) என்று அழைக்கப்படுகின்றன. ஒரு காரின் name மற்றும் top-speed-ஐக் கொண்டிருக்கும் Car என்ற நமது முதல் ரெக்கார்டை உருவாக்குவோம்:

(defrecord Car [name top-speed])

வெளியீடு (Output)

user.Car

வெளியீட்டில் நீங்கள் காண்பது போல, இது user.Car என்று அழைக்கப்படும் ஒன்றை உருவாக்குகிறது. user என்பது இயல்பான நேம்ஸ்பேஸ் (namespace), மற்றும் Car என்பது ரெக்கார்டைக் குறிக்கிறது. Car ஒரு சாதாரண மாறியாக இருந்திருந்தால், வெளியீடு user/Car என்பது போல இருந்திருக்கும்.

இப்போது tata-nano என்ற மாறியை வரையறுப்போம்:

(def tata-nano)

Car என்ற வகையில்:

(def tata-nano (->Car))

நாம் Car ரெக்கார்டை உருவாக்கியபோது, முதலில் name மற்றும் பின்னர் top-speed-ஐக் குறிப்பிட்டோம். அதே வரிசையைப் பின்பற்றி, tata-nano-விற்கு ஒரு பெயரைக் கொடுப்போம்:

(def tata-nano (->Car "Tata Nano"))

அதன் அதிகபட்ச வேகத்தை 120 என்று குறிப்பிடுவோம்:

(def tata-nano (->Car "Tata Nano" 120))

இப்போது மேலே உள்ள குறியீட்டை இயக்குவோம்:

(def tata-nano (->Car "Tata Nano" 120))

வெளியீடு (Output)

#'user/tata-nano

user நேம்ஸ்பேஸில் tata-nano என்ற மாறி உருவாக்கப்பட்டுள்ளது. இது ஒரு சாதாரண மதிப்பு அல்ல, மாறாக இரண்டு மதிப்புகளைக் கொண்ட ஒரு ஆப்ஜெக்ட் ஆகும்.

Clojure-ல் ஒரு ஆப்ஜெக்ட் என்பது ஒரு மேம்படுத்தப்பட்ட மேப் என்பதால், ஒரு மேப்பைப் போலவே நீங்கள் tata-nano-வின் :name-ஐ அணுகலாம்:

(:name tata-nano)

வெளியீடு (Output)

"Tata Nano"
(get tata-nano :name)

வெளியீடு (Output)

"Tata Nano"

அதேபோல், அதன் அதிகபட்ச வேகத்தை அணுகலாம்:

(:top-speed tata-nano)

வெளியீடு (Output)

120
(get tata-nano :top-speed)

வெளியீடு (Output)

120

அதேபோல், ambassador என்ற Car ரெக்கார்டின் மற்றொரு உதாரணத்தை உருவாக்குவோம், ஆனால் கீழே உள்ள குறியீட்டில் உள்ள வித்தியாசத்தைக் கவனியுங்கள்:

(def ambassador (map->Car {:name "Ambassador",
                           :top-speed 240 }))

வெளியீடு (Output)

#'user/ambassador

tata-nano-வை உருவாக்கியபோது, நாம் மதிப்புகளை வரிசைப்படி வழங்கினோம். இருப்பினும், map→Car-ஐப் பாருங்கள். →Car-க்கு பதிலாக, நாம் ஒரு மேப்பை அனுப்ப விரும்புவதாகக் குறிப்பிடுகிறோம், எனவே இந்த மேப்பை வழங்குகிறோம்:

{ :name "Ambassador",
  :top-speed 240 }

Clojure தானாகவே மேப்பைப் பிரித்து ambassador-ன் தொடர்புடைய புலங்களுக்கு (fields) மதிப்புகளை ஒதுக்குகிறது. நீங்கள் காட்டியது போல கீகளின் வரிசையை மாற்றலாம்:

{ :top-speed 240
  :name "Ambassador" }

இதுவும் ஒரே மாதிரியாகவே செயல்படும்.

இப்போது, ambassador-ன் :name மற்றும் :top-speed-ஐ மீட்டெடுப்போம்:

(:name ambassador)

வெளியீடு (Output)

"Ambassador"
(get ambassador :name)

வெளியீடு (Output)

"Ambassador"
(:top-speed ambassador)

வெளியீடு (Output)

240
(get ambassador :top-speed)

வெளியீடு (Output)

240

21.2. செயல்பாடுகளுடன் ரெக்கார்ட்ஸ்களைப் பயன்படுத்துதல் (Using Records with Functions)

இந்தப் பகுதிக்கு, நீங்கள் https://gitlab.com/clojure-book/code/-/raw/master/record_2.clj-ல் உள்ள குறியீட்டைப் பயன்படுத்தலாம். உங்கள் REPL டெர்மினலில் இயங்கினாலும் அல்லது உங்கள் IDE-உடன் ஒருங்கிணைக்கப்பட்டிருந்தாலும், வரிகளை ஒவ்வொன்றாக நகலெடுத்து (copy) பேஸ்ட் (paste) செய்யவும்.
இந்தப் பகுதிக்கான வீடியோவை இங்கே காணலாம்: https://yu7.in/clj-rec-fn

ரெக்கார்ட்களைக் கையாளத் தெரிந்த செயல்பாடுகளுக்கு அவற்றை அனுப்பலாம். கீழே, Car என்ற ரெக்கார்டை உருவாக்குகிறோம்.

(defrecord Car [name top-speed])

வெளியீடு (Output)

user.Car

Car-ன் ஒரு உதாரணமான tata-nano என்ற மாறியை உருவாக்குகிறோம்:

(def tata-nano (->Car "Tata Nano" 120))

வெளியீடு (Output)

#'user/tata-nano

இப்போது, ரெக்கார்டுடன் வேலை செய்யக்கூடிய ஒரு செயல்பாட்டை எழுதுவோம்; அதற்கு describe என்று பெயரிடுவோம்:

(defn describe [vehicle]
  (let [{name :name top-speed :top-speed} vehicle]
    (str name " has a top speed of " top-speed "Km/hr.")))

வெளியீடு (Output)

#'user/describe

describe செயல்பாடு vehicle என்ற ஆர்கியுமெண்ட்டை ஏற்கிறது. செயல்பாட்டின் உடலைப் பார்ப்போம்:

(let [{name :name top-speed :top-speed} vehicle]
    (str name " has a top speed of " top-speed "Km/hr."))

இங்கே, let [{name :name top-speed :top-speed} vehicle] என்பதைப் பயன்படுத்தி Car ரெக்கார்டை டிஸ்ட்ரக்சரிங் செய்கிறோம். இந்தத் துண்டில், வழங்கப்பட்ட Car ரெக்கார்டின் :name என்பது name என்ற மாறிக்கு ஒதுக்கப்படுகிறது, மற்றும் :top-speed என்பது top-speed-க்கு ஒதுக்கப்படுகிறது. பின்னர் (str name " has a top speed of " top-speed "Km/hr.") என்பதைப் பயன்படுத்தி காரை விவரிக்கும் ஒரு வடிவமைக்கப்பட்ட சரத்தைத் திருப்பிக் கொடுக்கிறோம், இது name மற்றும் top-speed-ஐ விவரிப்பில் இணைக்கிறது.

இப்போது, Car-ன் ஒரு உதாரணத்தில் describe-ஐப் பயன்படுத்துவோம்:

(describe tata-nano)

வெளியீடு (Output)

"Tata Nano has a top speed of 120Km/hr."

இப்போது, Ship என்ற புதிய ரெக்கார்டை உருவாக்குவோம்:

(defrecord Ship [name top-speed])

வெளியீடு (Output)

user.Ship

arctic-explorer என்ற புதிய கப்பல் உதாரணத்தை உருவாக்குவோம்:

(def arctic-explorer (->Ship "Arctic Explorer" 10))

வெளியீடு (Output)

#'user/arctic-explorer

கப்பலை விவரிக்கும் describe-ship என்ற செயல்பாட்டை எழுதுவோம்:

(defn describe-ship [vehicle]
  (let [{name :name top-speed :top-speed} vehicle]
    (str name " has a top speed of " top-speed " knots.")))

வெளியீடு (Output)

#'user/describe-ship

arctic-explorer-ல் describe-ship-ஐப் பயன்படுத்துவோம்

(describe-ship arctic-explorer)

வெளியீடு (Output)

"Arctic Explorer has a top speed of 10 knots."

இது வேலை செய்கிறது!

21.3. புரோட்டோகால்கள் (Protocols)

இந்தப் பகுதிக்கு, நீங்கள் https://gitlab.com/clojure-book/code/-/raw/master/protocol.clj-ல் உள்ள குறியீட்டைப் பயன்படுத்தலாம். உங்கள் REPL டெர்மினலில் இயங்கினாலும் அல்லது உங்கள் IDE-உடன் ஒருங்கிணைக்கப்பட்டிருந்தாலும், வரிகளை ஒவ்வொன்றாக நகலெடுத்து (copy) பேஸ்ட் (paste) செய்யவும்.
இந்தப் பகுதிக்கான வீடியோவை இங்கே காணலாம்: https://yu7.in/clj-protocol

முந்தைய பகுதியில், describe செயல்பாட்டைப் பயன்படுத்தி ஒரு Car-ஐயும், describe-ship செயல்பாட்டைப் பயன்படுத்தி ஒரு Ship-ஐயும் விவரித்ததைப்பார்த்தோம். ஒரு நிரலாளர் இந்த இரண்டிற்கும் ஒரே describe செயல்பாட்டைப் பயன்படுத்த முடிந்தால் அது மிகவும் வசதியாக இருக்கும். புரோட்டோகால்களைப் பயன்படுத்தி இதை எவ்வாறு சாதிப்பது என்பதைப் பார்ப்போம்.

காட்டியுள்ளபடி Describe என்ற புரோட்டோகாலை உருவாக்குவோம்:

(defprotocol Describe
  (describe [this]))

வெளியீடு (Output)

Describe

பின்வரும் குறியீட்டைப் பார்ப்போம்:

(defrecord Car [name top-speed]
  Describe
  (describe [this]
    (let [{name :name top-speed :top-speed} this]
      (str name " has a top speed of " top-speed "Km/hr."))))

வெளியீடு (Output)

user.Car

இந்த உதாரணத்தில், Car என்ற ரெக்கார்டை உருவாக்குகிறோம்:

(defrecord Car [name top-speed])

அடுத்து, Describe புரோட்டோகாலை இதில் இணைக்கிறோம்:

(defrecord Car [name top-speed]
  Describe)

பின்னர், Car-க்கான describe செயல்பாட்டின் செயலாக்கத்தை (implementation) வழங்குகிறோம்:

(defrecord Car [name top-speed]
  Describe
  (describe [this]
    (let [{name :name top-speed :top-speed} this]
      (str name " has a top speed of " top-speed "Km/hr."))))

இங்கே defn இல்லை என்பதைக் கவனியுங்கள். செயல்பாட்டின் பெயர் describe, அதைத் தொடர்ந்து ஆர்கியுமெண்ட் this வருகிறது, மேலும் describe-ன் உடலிலும், this-ஐ டிஸ்ட்ரக்சரிங் செய்து ஒரு சர விவரிப்பைத் திருப்பிக் கொடுக்கிறோம்.

இந்த பகுதியில் உள்ள உதாரணங்களில் பயன்படுத்தப்படும் this என்பது ஒரு சிறப்பு Clojure கீவேர்ட் அல்ல; அது ஒரு மாறிக்கு வழங்கப்பட்ட சாதாரண பெயர் மட்டுமே.

அதேபோல், Ship-க்கு, describe செயல்பாட்டை இவ்வாறு செயல்படுத்துகிறோம்:

(defrecord Ship [name top-speed]
  Describe
  (describe [this]
    (let [{name :name top-speed :top-speed} this]
      (str name " has a top speed of " top-speed " knots."))))

வெளியீடு (Output)

user.Ship

இப்போது, tata-nano என்ற Car உதாரணத்தை உருவாக்குவோம்:

(def tata-nano (->Car "Tata Nano" 120))

வெளியீடு (Output)

#'user/tata-nano

அடுத்து, arctic-explorer என்ற Ship உதாரணத்தை உருவாக்குவோம்:

(def arctic-explorer (->Ship "Arctic Explorer" 10))

வெளியீடு (Output)

#'user/arctic-explorer

நாம் tata-nano-வை describe-க்கு அனுப்பும்போது, அது Car வகையைச் சேர்ந்தது என்பதை அடையாளம் கண்டு, Car-க்குள் வரையறுக்கப்பட்ட describe செயலாக்கம் அழைக்கப்படுகிறது:

(describe tata-nano)

வெளியீடு (Output)

"Tata Nano has a top speed of 120Km/hr."

அதேபோல், arctic-explorer ஒரு Ship என்பதை அடையாளம் கண்டு, Ship-ன் சொந்த describe செயலாக்கம் கீழே உள்ள குறியீட்டில் அழைக்கப்படுகிறது:

(describe arctic-explorer)

வெளியீடு (Output)

"Arctic Explorer has a top speed of 10 knots."

21.3.1. புரோட்டோகால்களை விரிவாக்குதல் (Extending Protocols)

இந்தப் பகுதிக்கு, நீங்கள் https://gitlab.com/clojure-book/code/-/raw/master/extend-protocol.clj-ல் உள்ள குறியீட்டைப் பயன்படுத்தலாம். உங்கள் REPL டெர்மினலில் இயங்கினாலும் அல்லது உங்கள் IDE-உடன் ஒருங்கிணைக்கப்பட்டிருந்தாலும், வரிகளை ஒவ்வொன்றாக நகலெடுத்து (copy) பேஸ்ட் (paste) செய்யவும்.
இந்தப் பகுதிக்கான வீடியோவை இங்கே காணலாம் https://yu7.in/clj-ext-pcl

நமக்கு Describe என்ற புரோட்டோகால் உள்ளது:

(defprotocol Describe
  (describe [this]))

வெளியீடு (Output)

Describe

இதை Car ரெக்கார்டிற்கான describe செயல்பாட்டைச் செயல்படுத்தப் பயன்படுத்தினோம்:

(defrecord Car [name top-speed]
  Describe
  (describe [this]
    (let [{name :name top-speed :top-speed} this]
      (str name " has a top speed of " top-speed "Km/hr."))))

வெளியீடு (Output)

user.Car

மற்றும் அது வேலை செய்கிறது:

(def tata-nano (->Car "Tata Nano" 120))

வெளியீடு (Output)

#'user/tata-nano
(describe tata-nano)

வெளியீடு (Output)

"Tata Nano has a top speed of 120Km/hr."

வேறொருவரால் எழுதப்பட்ட குறியீட்டில் Rocket என்ற ரெக்கார்ட் வரையறுக்கப்பட்டுள்ளது என்று வைத்துக்கொள்வோம். அசல் Rocket ரெக்கார்டின் குறியீட்டை மாற்றாமல், அதற்காக Describe புரோட்டோகாலை விரிவாக்க விரும்புகிறோம்.

(defrecord Rocket [name orbit])

வெளியீடு (Output)

user.Rocket

இதற்காக, Clojure extend-protocol என்ற செயல்பாட்டை வழங்குகிறது, இதை கீழே காட்டியபடி Describe புரோட்டோகாலை விரிவாக்கப் பயன்படுத்தலாம்:

(extend-protocol Describe
  Rocket
   (describe [this] (str (:name this) " reaches " (:orbit this) " orbit.")))

வெளியீடு (Output)

nil

மேலே உள்ள குறியீட்டில், ஒரு புரோட்டோகாலை விரிவாக்க விரும்புவதை இவ்வாறு குறிப்பிடுகிறோம்:

(extend-protocol)

முதலில் ஆர்கியுமெண்டாக Describe-ஐ வழங்குவதன் மூலம் அதை விரிவாக்க விரும்புவதை குறிப்பிடுகிறோம்:

(extend-protocol Describe)

இரண்டாவது ஆர்கியுமெண்டாக, Rocket ரெக்கார்டிற்காக புரோட்டோகாலை விரிவாக்க விரும்புவதைக் குறிப்பிடுகிறோம்:

(extend-protocol Describe
  Rocket)

இறுதியாக, describe செயல்பாட்டின் செயலாக்கத்தை வரையறுக்கிறோம்:

(extend-protocol Describe
  Rocket
   (describe [this] (str (:name this) " reaches " (:orbit this) " orbit.")))

இப்போது, pslv என்ற Rocket உதாரணத்தை உருவாக்குவோம்:

(def pslv (->Rocket "PSLV" "Low Earth"))

வெளியீடு (Output)

#'user/pslv

இப்போது, அதை describe-க்கு அனுப்புவோம்:

(describe pslv)

வெளியீடு (Output)

"PSLV reaches Low Earth orbit."

இது வேலை செய்கிறது!

22. நேம்ஸ்பேஸ்கள் (Namespaces)

இதற்கான மூலக் குறியீட்டை (source code) இங்கே பெறலாம் https://gitlab.com/clojure-book/code/-/raw/master/name_space.clj
இந்தப் பகுதிக்கான வீடியோவை இங்கே காணலாம் https://yu7.in/clj-ns

ஒரே வார்த்தை சூழலைப் பொறுத்து வெவ்வேறு அர்த்தங்களைக் கொண்டிருக்கலாம். உதாரணமாக, ஒரு ஹோட்டலை 'booking' செய்வது ஒரு அர்த்தம், நூலகத்தில் உள்ள ஒரு 'book' என்பது மற்றொன்று, மற்றும் ஒரு காவல் நிலையத்தில் ஒருவரை 'book' செய்வது முற்றிலும் வேறொரு அர்த்தத்தைத் தரும். அதேபோல், புத்தகத்தில் உள்ள ஒரு 'page' என்பது இயங்குதளத்தில் உள்ள ஒரு 'page'-லிருந்து வேறுபட்டது, அது ஒரு நபரின் குடும்பப் பெயராக இருக்கும் 'Page'-லிருந்து மேலும் வேறுபட்டது.

குறியீட்டில் இத்தகைய குழப்பங்களைத் தீர்க்க, Clojure 'நேம்ஸ்பேஸ்கள்' (namespaces) என்ற அம்சத்தை வழங்குகிறது. கீழே உள்ள name_space.clj நிரலைப் பாருங்கள்; அதைத் தட்டச்சு செய்து இயக்கி முயற்சிக்கவும்.

;; name_space.clj

(ns lawyer)

(def about-me "I never speak truth.")

(ns politician)

(def about-me "I loot the nation.")

(ns engineer)

(def about-me "I apply science.")

(println lawyer/about-me)
(println politician/about-me)
(println about-me)

(ns politician)

(println about-me)

இதை clj பயன்படுத்தி இவ்வாறு இயக்கவும்:

$ clj name_space.clj

வெளியீடு (Output):

I never speak truth.
I loot the nation.
I apply science.
I loot the nation.

இப்போது மேலே உள்ள நிரல் எவ்வாறு செயல்படுகிறது என்று பார்ப்போம். இந்தக் குறியீட்டைப் பாருங்கள்:

(ns lawyer)

(def about-me "I never speak truth.")

(ns politician)

(def about-me "I loot the nation.")

(ns engineer)

(def about-me "I apply science.")

முதலில், நாம் lawyer என்ற நேம்ஸ்பேஸை உருவாக்குகிறோம்:

(ns lawyer)

அதில், நாம் about-me என்ற மாறியை ஒரு குறிப்பிட்ட மதிப்புடன் வரையறுக்கிறோம்:

(ns lawyer)

(def about-me "I never speak truth.")

அடுத்து, politician என்ற நேம்ஸ்பேஸை அதன் சொந்த about-me மாறியுடன் வரையறுக்கிறோம்:

(ns lawyer)

(def about-me "I never speak truth.")

(ns politician)

(def about-me "I loot the nation.")

lawyer நேம்ஸ்பேஸில் உள்ள about-me வரையறை, (def about-me "I loot the nation.") என்பதால் மேலெழுதப்படவில்லை (not overwritten), ஏனெனில் அவை வெவ்வேறு நேம்ஸ்பேஸ்களில் உள்ளன; வழக்கறிஞர் இன்னும் உண்மையைப் பேசவில்லை.

இப்போது engineer என்ற மற்றொரு நேம்ஸ்பேஸை உருவாக்கி, அந்த நேம்ஸ்பேஸிற்குள் மற்றொரு about-me-ஐ வரையறுப்போம்:

(ns lawyer)

(def about-me "I never speak truth.")

(ns politician)

(def about-me "I loot the nation.")

(ns engineer)

(def about-me "I apply science.")

இப்போது வெவ்வேறு நேம்ஸ்பேஸ்களில் உள்ள இந்த மூன்று வெவ்வேறு about-me வரையறைகளை எவ்வாறு அணுகுவது என்று பார்ப்போம்.

நாம் தற்போது engineer நேம்ஸ்பேஸில் இருக்கிறோம். lawyer நேம்ஸ்பேஸில் உள்ள about-me-ஐ அணுக, நாம் lawyer/about-me என்பதைப் பயன்படுத்த வேண்டும், இது கீழே உள்ள குறியீட்டின் கடைசி வரியில் காட்டப்பட்டுள்ளது:

(ns engineer)

(def about-me "I apply science.")

(println lawyer/about-me)

இது I never speak truth. என்று அச்சிடும். அதேபோல், கீழே உள்ள குறியீட்டின் கடைசி வரியைப் பாருங்கள்:

(ns engineer)

(def about-me "I apply science.")

(println lawyer/about-me)
(println politician/about-me)

politician/about-me-ஐ அச்சிடுவதன் மூலம், நாம் politician நேம்ஸ்பேஸில் உள்ள about-me-ன் மதிப்பை அணுகுகிறோம், இதன் விளைவாக டெர்மினலில் I loot the nation. என்ற வெளியீடு கிடைக்கிறது.

நாம் engineer நேம்ஸ்பேஸில் இருப்பதால், engineer/about-me என்பதைப் பயன்படுத்த வேண்டிய அவசியமில்லை, இருப்பினும் அவ்வாறு செய்வது குறியீட்டை இன்னும் வெளிப்படையானதாக (explicit) ஆக்கும். கீழே உள்ள பகுதியில் கடைசி வரியைப் பாருங்கள்:

(ns engineer)

(def about-me "I apply science.")

(println lawyer/about-me)
(println politician/about-me)
(println about-me)

இந்த about-me ஆனது engineer நேம்ஸ்பேஸிற்குள் அழைக்கப்படுவதால், இது I apply science. என்று அச்சிடுகிறது.

இறுதியாக, பின்வரும் வரியைப் பயன்படுத்தி நேம்ஸ்பேஸை politician-க்கு மாற்றுவோம்:

(ns politician)

இப்போது நாம் politician நேம்ஸ்பேஸிற்கு மாறியுள்ளதால், about-me-ஐ அச்சிடுவது:

(println about-me)

மீண்டும் I loot the nation. என்பதை அச்சிடுகிறது.

23. விதிவிலக்கு கையாளுதல் (Exception Handling)

இந்தப் பகுதிக்கான வீடியோவை இங்கே காணலாம் https://yu7.in/clj-exh

உலகம் மிகவும் பெரியது மற்றும் சிக்கலானது. நீங்கள் குறியீடு எழுதும்போது, உங்களுக்கு வழங்கப்பட்ட சிக்கலைச் சிறப்பாக விவரிக்கும் சில துல்லியமான விதிகளை மட்டுமே நீங்கள் கவனத்தில் கொள்கிறீர்கள், ஆனால் நீங்கள் உருவாக்கும் தர்க்கம் (logic) உலகின் தேவைகளை பூர்த்தி செய்யாமல் போக வாய்ப்புண்டு. எதிர்பாராத ஒன்று நடக்கலாம், அதை உங்கள் குறியீட்டில் நீங்கள் கையாள வேண்டியிருக்கலாம். விதிவிலக்கு கையாளுதலை (exception handling) வரவேற்கிறோம்.

உதாரணமாக, நீங்கள் ஒரு கோப்பிலிருந்து தரவைப் படிக்க விரும்புகிறீர்கள், ஆனால் அந்தக் கோப்பு இல்லை என்று வைத்துக்கொள்வோம். ஒரு கோப்பைப் படிக்க, நாம் (slurp "does_not_exist.txt")-ஐப் பயன்படுத்துகிறோம்; does_not_exist.txt என்ற கோப்பு இல்லாததால் இது ஒரு விதிவிலக்கை (exception) ஏற்படுத்தும். இதைக் கையாள, நாம் அதை ஒரு try பிளாக்கிற்குள் வைத்து, Clojure-ஐ இவ்வாறு முயற்சிக்கச் சொல்கிறோம்:

(try
  (slurp "does_not_exist.txt"))

இருப்பினும், ஒரு விதிவிலக்கு ஏற்படும்போது, அதை நாம் கைப்பற்ற வேண்டும். கீழே காட்டியுள்ளபடி (catch Exception e (println (str "caught exception: " (.getMessage e))))-ஐப் பயன்படுத்தலாம்:

(try
  (slurp "does_not_exist.txt")
  (catch Exception e (println (str "caught exception: " (.getMessage e)))))

வெளியீடு (Output)

caught exception: does_not_exist.txt (No such file or directory)

மேலே உள்ள குறியீட்டை இயக்கும்போது, நமக்கு மேலே உள்ள வெளியீடு கிடைக்கிறது. அதாவது, விதிவிலக்கு e என்ற மாறியில் கைப்பற்றப்படுகிறது, மேலும் நாம் (.getMessage e) என்பதைப் பயன்படுத்தி செய்தியை மீட்டெடுத்து அதை அச்சிடுகிறோம்.

ஒரு விதிவிலக்கு ஏற்படும்போதோ அல்லது குறியீடு முறிந்தாலோ, அதற்கு அடுத்ததாக உள்ள குறியீடுகள் இயங்காது. கீழே உள்ள குறியீட்டை இயக்கிப் பாருங்கள்:

(try
  (slurp "does_not_exist.txt")
  (println "this won't be printed")
  (catch Exception e (println (str "caught exception: " (.getMessage e)))))

வெளியீடு (Output)

caught exception: does_not_exist.txt (No such file or directory)

மேலே உள்ள உதாரணத்தில், (slurp "does_not_exist.txt") ஒரு விதிவிலக்கை ஏற்படுத்துவதால், (println "this won’t be printed") என்ற வரி இயங்காது. விதிவிலக்கு கைப்பற்றப்பட்டு, (catch Exception e (println (str "caught exception: " (.getMessage e)))) மூலம் செய்தி அச்சிடப்படுகிறது.

ஒரு விதிவிலக்கு ஏற்படும்போது, நீங்கள் இன்னும் ஒரு செயலைச் செய்ய வேண்டும் என்று வைத்துக்கொள்வோம்—உதாரணமாக, புரோகிராமருக்குத் தெரியப்படுத்த அதை ஒரு கோப்பில் பதிவு (logging) செய்வது. இதை finally கிளாஸைக் கொண்ட ஒரு வடிவத்தைப் பயன்படுத்திச் செய்யலாம்:

(try
  (slurp "does_not_exist.txt")
  (println "this won't be printed")
  (catch Exception e (println (str "caught exception: " (.getMessage e))))
  (finally (println "code something that will deal with this exception")))

வெளியீடு (Output)

caught exception: does_not_exist.txt (No such file or directory)
code something that will deal with this exception

விதிவிலக்கு ஏற்பட்டாலும் finally வடிவம் இயக்கப்படுகிறது, அதனால்தான் code something that will deal with this exception என்ற சரம் அச்சிடப்படுவதைக் காண்கிறோம்.

இதுவரை, நாம் மிகவும் பொதுவான வடிவத்தைப் பயன்படுத்தி விதிவிலக்குகளைக் கைப்பற்றினோம்: (catch Exception e (println (str "caught exception: " (.getMessage e)))). இருப்பினும், உண்மையான குறியீட்டுச் சூழலில், விதிவிலக்குகளை இன்னும் குறிப்பிட்ட வடிவத்தில் கைப்பற்றினால் அது உதவியாக இருக்கும். உதாரணமாக, நீங்கள் ஒரு கோப்பைத் திறக்க முயற்சித்தால், குறிப்பாக FileNotFoundException உள்ளதா என்று சரிபார்க்க வேண்டும்.

FileNotFoundException எங்கிருந்து வருகிறது என்று நீங்கள் யோசிக்கலாம். நீங்கள் https://docs.oracle.com/javase/8/docs/api/java/io/package-summary.html பக்கத்தைப் பார்த்து "Exception Summary" பகுதியைத் தேடலாம்.

கீழே உள்ள குறியீட்டைப் பார்த்து இயக்கவும்:

(try
  (slurp "does_not_exist.txt")
  (println "this won't be printed")
  (catch java.io.FileNotFoundException e (println (str "caught exception: " (.getMessage e))))
  (finally (println "code something that will deal with this exception")))

வெளியீடு (Output)

caught exception: does_not_exist.txt (No such file or directory)
code something that will deal with this exception

இங்கே, பொதுவான Exception கேட்ச்-க்கு பதிலாக, நாம் (catch java.io.FileNotFoundException e (println (str "caught exception: " (.getMessage e))))-ஐப் பயன்படுத்துகிறோம், மேலும் இது வேலை செய்கிறது. ஒரு சிறந்த புரோகிராமர் தனது குறியீட்டில் என்ன தவறுகள் நடக்கக்கூடும் என்பதை முன்கூட்டியே கணித்து, குறிப்பிட்ட விதிவிலக்குகளைக் கைப்பற்றி அவற்றைச் சரியாகக் கையாள வேண்டும்.

ஒரு try பிளாக்கில் பல விதிவிலக்குகளைக் கைப்பற்ற முடியும். கீழே உள்ள உதாரணத்தில், NumberFormatException, NullPointerException மற்றும் விடுபட்ட மற்ற அனைத்தையும் கைப்பற்ற ஒரு பொதுவான Exception-ஐப் பயன்படுத்துகிறோம். குறியீட்டை இயக்கிப் பார்ப்போம்:

(try
  ;; Code that might throw an exception
  (let [x (Integer/parseInt "not-a-number")]
    (println x))

  (catch NumberFormatException e
    (println "Caught a NumberFormatException:" (.getMessage e)))

  (catch NullPointerException e
    (println "Caught a NullPointerException:" (.getMessage e)))

  (catch Exception e
    (println "Caught a generic exception:" (.getMessage e))))

வெளியீடு (Output)

Caught a NumberFormatException: For input string: "not-a-number"

எனவே, "not-a-number" என்ற சரத்தை ஒரு முழு எண்ணாக (integer) மாற்ற முடியாது என்பதால், NumberFormatException ஏற்படுகிறது. இப்போது NullPointerException மற்றும் Exception ஆகியவற்றை உள்ளடக்கிய அதே குறியீட்டை கீழே உள்ளபடி முயற்சிப்போம்:

(defn print-length [s]
  (println "Length of the string is:" (.length s)))

;; Calling the function with a non-null string
(print-length "Hello, World!") ;; Works fine

;; Calling the function with nil
(print-length nil)

(try
  ;; Code that might throw an exception
  (print-length nil)

  (catch NumberFormatException e
    (println "Caught a NumberFormatException:" (.getMessage e)))

  (catch NullPointerException e
    (println "Caught a NullPointerException:" (.getMessage e)))

  (catch Exception e
    (println "Caught a generic exception:" (.getMessage e))))

வெளியீடு (Output)

Caught a NullPointerException: nil

மேலே உள்ள குறியீடு இயக்கப்பட்டபோது, print-length செயல்பாட்டிற்கு nil வழங்கப்பட்டதால், அதன் நீளத்தை (.length s) மூலம் கணக்கிட முடியாது, எனவே ஒரு NullPointerException ஏற்படுகிறது. இதன் விளைவாக, இந்த குறியீட்டுப் பகுதி:

(catch NullPointerException e
    (println "Caught a NullPointerException:" (.getMessage e)))

இயக்கப்பட்டு, Caught a NullPointerException: nil என்று அச்சிடப்படுகிறது.

24. டெஸ்டிங் (Testing)

இந்தப் பகுதிக்கான வீடியோவை இங்கே காணலாம்: https://yu7.in/clj-test

டெஸ்டிங் என்பது கோடிங்கின் ஒரு ஒருங்கிணைந்த பகுதியாகும். நீங்கள் மில்லியன் கணக்கான முறை பயன்படுத்தப்படும் ஒரு குறியீட்டை எழுதியுள்ளீர்கள் என்று வைத்துக்கொள்வோம். உங்கள் சக ஊழியர் அதை மாற்றிய modifying செய்து, சில குறிப்பிட்ட நிகழ்வுகளில் அது தோல்வியடைந்தால், அவர்கள் அந்தத் தோல்விகளை எவ்வாறு அடையாளம் காண முடியும்? நீங்கள் குறியீட்டை எழுதும்போது, அதற்கான டெஸ்ட்களையும் எழுத வேண்டும். இதன் மூலம் யாராவது உங்கள் குறியீட்டை மாற்றும்போது, அனைத்தும் இன்னும் சரியாக வேலை செய்கிறதா என்பதை உறுதிப்படுத்த அவர்கள் தொடர்புடைய டெஸ்ட்களை இயக்க முடியும்.

டெஸ்ட்கள் குறியீடாக எழுதப்படுவதால், டெஸ்டிங் தானியங்கி முறையில் (automated) நடைபெறுகிறது. கணினிகளால் இந்த டெஸ்ட்களை மிக வேகமாக இயக்க முடியும் என்பதால், இது கணிசமான நேரத்தைச் சேமிக்கிறது.

ஒரு வலுவான டெஸ்ட் சூட் (test suite), உங்கள் குறியீடு இன்னும் எதிர்பார்த்தபடி வேலை செய்கிறது என்பதை டெஸ்ட்கள் உறுதி செய்யும் என்ற நம்பிக்கையுடன், உங்கள் குறியீட்டை ரீஃபாக்டர் (refactor) செய்ய அனுமதிக்கிறது.

கீழே காட்டப்பட்டுள்ள குறியீட்டைப் பார்ப்போம்:

;; calc_lib.clj

(defn add [a b]
  (+ a b))

(defn sub [a b]
  (- a b))

இந்தக் குறியீடு calc_lib.clj என்ற கோப்பில் உள்ளது மற்றும் இரண்டு எண்களைக் கூட்டவும் கழிக்கவும் செய்யும் இரண்டு செயல்பாடுகளைக் கொண்டுள்ளது. இப்போது, இதற்கான டெஸ்ட்களை எழுதுவோம்.

முதலில், calc_lib_test.clj என்ற Clojure கோப்பை உருவாக்குவோம். நாம் டெஸ்ட் செய்யப்போகும் கோப்பு calc_lib.clj மற்றும் அதற்குரிய டெஸ்ட் கோப்பு calc_lib_test.clj என்பதைக் கவனியுங்கள். இது ஒரு டெஸ்ட் கோப்பு என்பதைக் குறிக்க கோப்பு பெயருடன் _test என்பதை எளிதாகச் சேர்த்துள்ளோம்.

;; calc_lib_test.clj

Clojure டெஸ்ட் லைப்ரரியிலிருந்து [9], நாம் மூன்று செயல்பாடுகளைப் பயன்படுத்துவோம்: deftest, இது ஒரு டெஸ்டை வரையறுக்கப் பயன்படுகிறது; testing, இது deftest-ன் உள்ளே ஒரு பெயரிடப்பட்ட துணைப் பகுதியை (subspace) உருவாக்கப் பயன்படுகிறது; மற்றும் is, இது ஒரு நிபந்தனையை உறுதிப்படுத்தவும் (assert), அந்த உறுதிப்படுத்தல் தோல்வியுற்றால் ஒரு செய்தியை அச்சிடவும் பயன்படுகிறது.

நமது கோப்பு இப்போது இவ்வாறு выглядит:

;; calc_lib_test.clj

(require '[clojure.test :refer [deftest is testing]])

அடுத்து, நாம் டெஸ்ட் செய்ய விரும்பும் calc_lib.clj கோப்பை லோடு செய்வோம்:

;; calc_lib_test.clj

(require '[clojure.test :refer [deftest is testing]])

(load-file "calc_lib.clj")

இப்போது, calc_lib_test என்ற ஒரு டெஸ்ட் செயல்பாட்டை வரையறுப்போம்:

;; calc_lib_test.clj

(require '[clojure.test :refer [deftest is testing]])

(load-file "calc_lib.clj")

; nest within `deftest` in source file
(deftest calc_lib_test)

மேலே காட்டப்பட்டுள்ளபடி, இது (deftest calc_lib_test) என்பதைப் பயன்படுத்தி வரையறுக்கப்பட்டுள்ளது. இப்போது, நாம் எதை டெஸ்ட் செய்கிறோம் என்பதற்கான விளக்கமாக "calc_lib" என்பதை (testing "calc_lib")-ஐப் பயன்படுத்திச் சேர்ப்போம்:

;; calc_lib_test.clj

(require '[clojure.test :refer [deftest is testing]])

(load-file "calc_lib.clj")

; nest within `deftest` in source file
(deftest calc_lib_test
  (testing "calc_lib"))

இப்போது, மற்றொரு testing பிளாக்கைச் சேர்ப்பதன் மூலம் add செயல்பாட்டை முதலில் டெஸ்ட் செய்வோம்:

;; calc_lib_test.clj

(require '[clojure.test :refer [deftest is testing]])

(load-file "calc_lib.clj")

; nest within `deftest` in source file
(deftest calc_lib_test
  (testing "calc_lib"
    (testing "add")))

(add 2 2) என்பது 4-க்குச் சமம் என்பதை நாம் உறுதிப்படுத்த விரும்புகிறோம்:

(= 4 (add 2 2))

இப்போது, இந்த உறுதிப்படுத்தலை (assertion) is செயல்பாட்டிற்கு வழங்குகிறோம்:

(is (= 4 (add 2 2)))

இறுதியாக, "add" டெஸ்டை விவரிக்கும் testing செயல்பாட்டிற்கு இதை இரண்டாவது ஆர்கியுமெண்டாக வழங்குகிறோம்:

;; calc_lib_test.clj

(require '[clojure.test :refer [deftest is testing]])

(load-file "calc_lib.clj")

; nest within `deftest` in source file
(deftest calc_lib_test
  (testing "calc_lib"
    (testing "add"
      (is (= 4 (add 2 2))))))

இப்போது, டெஸ்டை இயக்க calc_lib_test-ஐ அழைப்போம்:

;; calc_lib_test.clj

(require '[clojure.test :refer [deftest is testing]])

(load-file "calc_lib.clj")

; nest within `deftest` in source file
(deftest calc_lib_test
  (testing "calc_lib"
    (testing "add"
      (is (= 4 (add 2 2))))))

(calc_lib_test)

clj-ஐப் பயன்படுத்தி கோப்பை இயக்கவும்:

$ clj calc_lib_test.clj

டெஸ்ட் வெற்றி பெற்றதால் எதுவும் அச்சிடப்படவில்லை.

இப்போது, தோல்வியை உருவகப்படுத்த குறியீட்டில் 4-ஐ 5 என மாற்றவும்:

;; calc_lib_test.clj

(require '[clojure.test :refer [deftest is testing]])

(load-file "calc_lib.clj")

; nest within `deftest` in source file
(deftest calc_lib_test
  (testing "calc_lib"
    (testing "add"
      (is (= 5 (add 2 2))))))

(calc_lib_test)

கோப்பை மீண்டும் இயக்கவும்:

$ clj calc_lib_test.clj
WARNING: Implicit use of clojure.main with options is deprecated, use -M

FAIL in (calc_lib_test) (calc_lib_test.clj:11)
calc_lib add
expected: (= 5 (add 2 2))
  actual: (not (= 5 4))

நீங்கள் காண்பது போல, உறுதிப்படுத்தல் தோல்வியடைந்ததால், calc_lib மற்றும் add-ன் உள்ளே ஒரு உறுதிப்படுத்தல் தோல்வியுற்றது என்பதை வெளியீடு குறிப்பிடுகிறது, மேலும் எதிர்பார்க்கப்பட்ட மற்றும் உண்மையான முடிவுகள் இரண்டையும் காட்டுகிறது.

testing பிளாக்குகளில் நீங்கள் எவ்வளவு விரிவான விளக்கங்களை வழங்குகிறீர்களோ, அவ்வளவு எளிதாக என்ன தவறு நடந்தது என்பதை அடையாளம் காண முடியும். தோல்வியுற்ற வரியின் எண்ணும் அச்சிடப்படுகிறது.

;; calc_lib_test.clj

(require '[clojure.test :refer [deftest is testing]])

(load-file "calc_lib.clj")

; nest within `deftest` in source file
(deftest calc_lib_test
  (testing "calc_lib"
    (testing "add"
      (is (= 5 (add 2 2)) "adding 2 and 2 should give the correct output"))))

(calc_lib_test)

டெஸ்டை விவரிப்பதற்காக is செயல்பாட்டிற்கு இரண்டாவது ஆர்கியுமெண்ட்டைச் சேர்த்துள்ளோம். கோப்பை இயக்குவோம்:

$ clj calc_lib_test.clj
WARNING: Implicit use of clojure.main with options is deprecated, use -M

FAIL in (calc_lib_test) (calc_lib_test.clj:11)
calc_lib add
adding 2 and 2 should give the correct output
expected: (= 5 (add 2 2))
  actual: (not (= 5 4))

ஒரு டெஸ்ட் தோல்வியடையும் போது, is-க்கு வழங்கப்பட்ட விளக்கம் அச்சிடப்படுகிறது, இது டிபக்கிங்கிற்கு மேலும் குறிப்புகளை வழங்குகிறது.

மீண்டும் வெற்றி பெறும் குறியீட்டிற்குத் திரும்புவோம்:

;; calc_lib_test.clj

(require '[clojure.test :refer [deftest is testing]])

(load-file "calc_lib.clj")

; nest within `deftest` in source file
(deftest calc_lib_test
  (testing "calc_lib"
    (testing "add"
      (is (= 4 (add 2 2))))))

(calc_lib_test)

இப்போது, 3 மற்றும் 4-ன் கூடுதல் 7 ஆக உள்ளதா என்று சரிபார்க்க இரண்டாவது உறுதிப்படுத்தலைச் சேர்ப்போம்:

;; calc_lib_test.clj

(require '[clojure.test :refer [deftest is testing]])

(load-file "calc_lib.clj")

; nest within `deftest` in source file
(deftest calc_lib_test
  (testing "calc_lib"
    (testing "add"
      (is (= 4 (add 2 2)))
      (is (= 7 (add 3 4))))))

(calc_lib_test)

இறுதியாக, மற்றொரு testing பிளாக்கைச் சேர்ப்பதன் மூலம் sub செயல்பாட்டை டெஸ்ட் செய்யலாம்:

;; calc_lib_test.clj

(require '[clojure.test :refer [deftest is testing]])

(load-file "calc_lib.clj")

; nest within `deftest` in source file
(deftest calc_lib_test
  (testing "calc_lib"
    (testing "add"
      (is (= 4 (add 2 2)))
      (is (= 7 (add 3 4))))
    (testing "sub"
      (is (= 0 (sub -2 -2)))
      (is (= 7 (sub 3 -4))))))

(calc_lib_test)

25. மேக்ரோக்கள் (Macros)

இந்தப் பகுதிக்கான குறியீட்டை இங்கே பெறவும் https://gitlab.com/clojure-book/code/-/raw/master/macro.clj
இந்தப் பகுதிக்கான வீடியோவை இங்கே காணலாம் https://yu7.in/clj-macros

நீங்கள் 2 மற்றும் 3-ஐக் கூட்ட விரும்பினால், அதை இவ்வாறு எழுதுவீர்கள்:

(+ 2 3)

இங்கு ` என்பது ஒரு செயல்பாடு (function) மற்றும் `2` மற்றும் `3` ஆகியவை ஆர்கியுமெண்டுகள். ஆனால், நான் `( 2 3) என்பதற்குப் பதிலாக (2 + 3) என்று எழுதுவது எனக்கு வசதியாக இருக்கிறது என்றால், அதை Clojure-ல் எப்படிச் செய்வது?

(+ 2 3) போன்ற எந்தவொரு Clojure குறியீட்டையும் நீங்கள் கவனித்தால், அது அடிப்படையில் ஒரு பட்டியல் (list) ஆகும். இதை நீங்கள் REPL-ல் சரிபார்க்கலாம்:

user=> (type '(+ 2 3))
clojure.lang.PersistentList

இங்குள்ள ஒற்றை மேற்கோள் குறி '(+ 2 3) என்பது அந்தப் பட்டியலை இயக்க வேண்டாம் என்று Clojure-க்கு சொல்கிறது. அடிப்படையான விஷயம் என்னவென்றால், Clojure-ல் எந்தவொரு நிரலும் ஒரு பட்டியல்தான். எனவே (2 + 3) என்பதும் ஒரு பட்டியல் தான். இப்போது நாம் செய்ய வேண்டியதெல்லாம் (2 + 3)-ஐ (+ 2 3) என்று மாற்றி அதை இயக்க வைப்பதே ஆகும்.

எனவே (2 + 3)-ஐ a-list என்ற மாறியில் சேமிப்பதாகக் கற்பனை செய்வோம்:

(def a-list '(2 + 3))

முதல் உறுப்பை எடுப்போம்:

(first a-list) ; இது 2-வை வழங்கும்

பிறகு முதல் உறுப்பிற்குப் பிறகு கடைசி உறுப்பை எடுப்போம்:

(first a-list) ; இது 2-வை வழங்கும்
(last a-list)  ; இது 3-ஐ வழங்கும்

நமக்கு பிளஸ் குறியைக் கைப்பற்றி முன்னால் வைக்க வேண்டும், மேலும் (2 + 3) என்ற a-list-ல் பிளஸ் குறியீடு நடுவில் இருப்பதை நாம் காண்கிறோம், அதாவது + என்பது இரண்டாவது உறுப்பு:

(second a-list)
(first a-list) ; இது 2-வை வழங்கும்
(last a-list)  ; இது 3-ஐ வழங்கும்

இப்போது இதை ஒரு பட்டியலாக மாற்றுவோம்:

(list ; (2 + 3)-ஐ (+ 2 3)-ஆக மாற்றுகிறது
  (second a-list)
  (first a-list)
  (last a-list))

மேலே உள்ள விஷயத்தை calculate என்ற மேக்ரோவாக வரையறுப்போம், இது a-list என்ற ஒற்றை ஆர்கியுமெண்ட்டை உள்ளீடாக ஏற்கும்:

(defmacro calculate [a-list]
  (list ; (2 + 3)-ஐ (+ 2 3)-ஆக மாற்றுகிறது
    (second a-list)
    (first a-list)
    (last a-list)))

இப்போது இந்த குறியீட்டை முயற்சி செய்யலாம்:

;; macro.clj

(defmacro calculate [a-list]
  (list ; (2 + 3)-ஐ (+ 2 3)-ஆக மாற்றுகிறது
    (second a-list)
    (first a-list)
    (last a-list)))

(println
  (calculate (2 + 3)))

இது வெளியீடாக 5-ஐத் தரும்.

ஒரு மேக்ரோவை விரிவுபடுத்த macroexpand என்ற கீவேர்டையும் நீங்கள் பயன்படுத்தலாம், கீழே உள்ள குறியீட்டைப் பாருங்கள்:

(println
  (macroexpand
    '(calculate (2 + 3))))

இது இவ்வாறு அச்சிடும்:

(+ 2 3)

நீங்கள் (2 + 3)-ஐ உள்ளீடாகக் கொடுக்கும்போது calculate மேக்ரோ செய்ய வேண்டியது இதுதான்.

முழுமையான macro.clj குறியீடு கீழே கொடுக்கப்பட்டுள்ளது:

;; macro.clj

(defmacro calculate [a-list]
  (list ; (2 + 3)-ஐ (+ 2 3)-ஆக மாற்றுகிறது
    (second a-list)
    (first a-list)
    (last a-list)))

(println
  (calculate (2 + 3)))

(println
  (macroexpand
    '(calculate (2 + 3))))

இதை இயக்கினால் உங்களுக்கு இவ்வாறு வெளியீடு கிடைக்கும்:

5
(+ 2 3)

25.1. டெம்ப்ளேட்டுகள் (Templates)

இந்தப் பகுதிக்கான குறியீட்டை இங்கே பெறவும் https://yu7.in/iD9KAU
இந்தப் பகுதிக்கான வீடியோவை இங்கே காணலாம் https://yu7.in/Q39kCj

மேக்ரோக்களை எழுதுவதற்கு ஒரு சிறந்த வழி உள்ளது, அதுதான் டெம்ப்ளேட்டுகளைப் (templates) பயன்படுத்துவது. இந்தப் பகுதியில் அது எப்படி செய்யப்படுகிறது என்று பார்ப்போம்.

Clojure-ல் if முறை உள்ளது என்று உங்களுக்குத் தெரியும், அதை இவ்வாறு பயன்படுத்தலாம்:

(if (even? 1) "even" "odd")

வெளியீடு (Output)

odd

எனவே (even? 1) என்பது true ஆக இருந்தால் "even" என்பதைத் திருப்பும், இல்லையெனில் "odd" என்பதைத் திருப்பும். அதாவது நிபந்தனை உண்மையாக இருந்தால் இரண்டாவது ஆர்கியுமெண்ட் திரும்பும், இல்லையெனில் மூன்றாவது ஆர்கியுமெண்ட் திரும்பும்.

இப்போது ஒரு unless மேக்ரோவை உருவாக்குவோம், அது இவ்வாறு இருக்கும்:

(defmacro unless [pred then else]
  (list 'if (list 'not pred) then else))

மேலே பார்த்தபடி, கணிப்பு (prediction) உண்மையாக இல்லையென்றால், அது இரண்டாவது ஆர்கியுமெண்ட்டைத் திருப்பும், இல்லையெனில் மூன்றாவது ஆர்கியுமெண்ட்டைத் திருப்பும். இது if-க்கு நேர் எதிரானது. இதை இயக்கிப் பார்ப்போம்:

(unless (even? 1) "odd" "even")

வெளியீடு (Output)

odd

இது வேலை செய்வது போல் தெரிகிறது.

இதை எழுதுவதற்கு இன்னும் எளிமையான வழி உள்ளது என்று சொன்னால்? கீழே உள்ள குறியீட்டைப் பாருங்கள்:

(defmacro unless-templating [pred then else]
  `(if (not ~pred) ~then ~else))
(unless-templating (even? 1) "odd" "even")

வெளியீடு (Output):

odd

இதை இயக்கிப் பார்த்தால் வேலை செய்வது போல் தெரிகிறது. எனவே இதை எப்படி எழுதுவது என்று பார்ப்போம்.

முதலில் நமக்கு இந்த defmacro உள்ளது, இது ஒரு மேக்ரோவை வரையறுக்கப் பயன்படுகிறது:

(defmacro)

பிறகு மேக்ரோவின் பெயர் unless-templating:

(defmacro unless-templating)

பிறகு ஆர்கியுமெண்டுகள், அவை pred, then மற்றும் else:

(defmacro unless-templating [pred then else])

இப்போது நாம் திருப்பிக் கொடுக்க வேண்டியது:

(if (not pred) then else)

ஆனால் இது இயக்கப்படக்கூடாது, இது கட்டமைக்கப்பட்டு திருப்பிக் கொடுக்கப்பட வேண்டும், எனவே நாம் பேக்டிக் (back tick) ` (இதை quote என்று அழைப்பார்கள்) பயன்படுத்துகிறோம், அப்போது அது இவ்வாறு அமையும்:

(defmacro unless-templating [pred then else]
  `(if (not pred) then else))

இப்போது நான் unless-templating மேக்ரோவை முயற்சிக்கும்போது:

(unless-templating (even? 1) "odd" "even")

வெளியீடு (Output)

Syntax error compiling at (.calva/output-window/output.calva-repl:69:1).
No such var: user/pred

எனக்கு மேலே உள்ள பிழை கிடைக்கிறது. அதற்குக் காரணம் எனது மேக்ரோ வரையறையில், நான் pred, then, மற்றும் else-ஐ மாறிகளாகக் கருதுகிறேன், ஆனால் அவை உண்மையில் உருவாக்கப்பட்ட குறியீட்டில் செருகப்பட வேண்டிய மேக்ரோ ஆர்கியுமெண்டுகள் ஆகும். பேக்டிக்-கோட் செய்யப்பட்ட வெளிப்பாட்டில், இந்த ஆர்கியுமெண்டுகளை ~ பயன்படுத்தி அன்-கோட் (unquote) செய்ய வேண்டும். இதோ திருத்தப்பட்ட குறியீடு:

(defmacro unless-templating [pred then else]
  `(if (not ~pred) ~then ~else))

இப்போது நான் unless-templating மேக்ரோவை முயற்சிக்கும்போது:

(unless-templating (even? 1) "odd" "even")

வெளியீடு (Output)

odd

இது வேலை செய்கிறது!

26. ஜாவாவின் சக்தி (Java Power)

இந்தப் பகுதிக்கான குறியீட்டை இங்கே பெறலாம் https://yu7.in/YYg1Lt
இந்தப் பகுதிக்கான வீடியோவை இங்கே பார்க்கலாம் https://yu7.in/iWM6PT. இந்தப் புத்தகத்தின் இந்தப் பகுதிக்கு என பிரத்யேக வீடியோவை விரைவில் பதிவேற்றுவேன்.

Clojure ஒரு ஹோஸ்டட் மொழி (hosted language), அதாவது இது தான் இலக்காகக் கொண்ட மொழியை நேர்த்தியாகச் சுற்றி இயங்குகிறது. பாரம்பரியமாக, ஜாவா (Java) அதன் ஹோஸ்ட் மொழியாக உள்ளது. ஜாவாஸ்கிரிப்ட், டார்ட், .NET, C++ போன்ற மொழிகளைச் சுற்றி இயங்கும் Clojure-ன் பிற வடிவங்களும் உள்ளன. இந்தப் புத்தகம் JVM (Java Virtual Machine)-ல் இயங்கும் Clojure-ஐப் பற்றி மட்டுமே கவனம் செலுத்துகிறது.

Clojure தான் இயங்கும் அடிப்படை மொழியின் சக்தியைப் பயன்படுத்த முடியும். பின்வரும் பக்கங்களில், உங்கள் Clojure குறியீட்டில் இருந்து ஜாவாவின் பரந்த சுற்றுச்சூழல் (Java ecosystem)-ன் சக்தியை எவ்வாறு பயன்படுத்துவது என்று பார்ப்போம்.

முதலில், power_of_java.clj என்ற Clojure கோப்பை உருவாக்கி, அதில் ஒரு நேம்ஸ்பேஸை (namespace) சேர்க்கவும்.

(ns power-of-java)

இப்போது, ஜாவாவில் உள்ளமைக்கப்பட்ட LocalDate [10] என்ற ஜாவா கிளாஸ் உள்ளது. அதை நமது Clojure கோப்பில் இம்போர்ட் (import) செய்வோம்:

(ns power-of-java
  (:import [java.time LocalDate]))

இந்தக் குறியீட்டை REPL-ல் இயக்கவும்.

வெளியீடு (Output)

nil

இப்போது LocalDate-ன் ஒரு இன்ஸ்டன்ஸை (instance) உருவாக்குவோம்:

(. LocalDate now)

வெளியீடு (Output)

#object[java.time.LocalDate 0x5a17e7da "2025-08-23"]

மேலே உள்ள குறியீட்டைப் பார்த்தால், நமக்கு ஒரு பட்டியல் () உள்ளது. LocalDate கிளாஸைப் பயன்படுத்த, அதை டாட் ஆபரேட்டரின் (dot operator) முதல் ஆர்கியுமெண்ட்டாக வழங்குகிறோம்: (. LocalDate). LocalDate-ல் உள்ள now() மெத்தட் தற்போதைய தேதியைத் தருவதால், அதை இவ்வாறு அழைக்கிறோம்: (. LocalDate now).

இதை அழைக்க மற்றொரு, மிகவும் முறையான (idiomatic) வழி உள்ளது:

(LocalDate/now)

வெளியீடு (Output)

#object[java.time.LocalDate 0xc0d150b "2025-08-23"]

(. LocalDate now) என்பதை விட (LocalDate/now) என்பது மிகவும் எளிமையாகத் தோன்றுகிறது அல்லவா? இனிமேல் இந்த முறையையே பயன்படுத்துவோம்.

இப்போது, 2025-07-05 போன்ற ஒரு குறிப்பிட்ட தேதிக்கு LocalDate-ன் ஒரு இன்ஸ்டன்ஸை உருவாக்க விரும்பினால், of மெத்தடை இவ்வாறு பயன்படுத்தலாம்:

(. LocalDate (of 2025 7 5))

வெளியீடு (Output)

#object[java.time.LocalDate 0x4a379f2c "2025-07-05"]

மேலே உள்ள குறியீட்டில், 2025 என்பது ஆண்டு, 7 என்பது மாதம், மற்றும் 5 என்பது தேதி. இவை of மெத்தடிற்கு ஆர்கியுமெண்டுகளாக வழங்கப்படுகின்றன. அதேபோல், 2025-07-10-க்கான LocalDate இன்ஸ்டன்ஸை உருவாக்க, of மெத்தடை இவ்வாறு பயன்படுத்தலாம்:

(. LocalDate (of 2025 7 10))

வெளியீடு (Output)

#object[java.time.LocalDate 0x1d897344 "2025-07-10"]

மேலே உள்ள குறியீட்டிற்குப் பதிலாக, of மெத்தடை இவ்வாறு பயன்படுத்தலாம்:

(LocalDate/of 2025 7 10)

வெளியீடு (Output)

#object[java.time.LocalDate 0x4ad9050b "2025-07-10"]

start-date மற்றும் end-date என்ற இரண்டு தேதிகளை வரையறுப்போம்:

(def start-date (LocalDate/of 2025 7 5))
(def end-date (LocalDate/of 2025 7 10))

now மற்றும் of ஆகியவை ஸ்டேடிக் கிளாஸ் மெத்தட்கள் (static class methods) ஆகும். இப்போது, LocalDate இன்ஸ்டன்ஸில் ஒரு நாளைச் சேர்க்க விரும்பினால், plusDays மெத்தடை இவ்வாறு பயன்படுத்தலாம்:

(.plusDays (LocalDate/now) 1)

வெளியீடு (Output)

#object[java.time.LocalDate 0x1a3a0c1b "2025-08-24"]

plusDays என்பது LocalDate-ன் ஒரு இன்ஸ்டன்ஸில் செயல்படுகிறது, எனவே அதை இவ்வாறு அழைக்கிறோம்: (.plusDays (LocalDate/now) 1). plusDays போன்ற செயல்பாடுகள் இன்ஸ்டன்ஸ் மெத்தட்கள் (instance methods) என்று அழைக்கப்படுகின்றன.

நாம் LocalDate கிளாஸைப் பயன்படுத்தாமல் plusDays செயல்பாட்டை நேரடியாகப் பயன்படுத்துவதைக் கவனியுங்கள். நாம் அதை இவ்வாறு எழுதவில்லை:

(LocalDate/plusDays (LocalDate/now) 1)

ஏனெனில் (LocalDate/now) என்பது LocalDate-ன் ஒரு இன்ஸ்டன்ஸைத் திருப்பித் தருகிறது, மேலும் அந்த இன்ஸ்டன்ஸின் வகையை (type) Clojure அறிந்திருப்பதால், LocalDate கிளாஸிலிருந்து plusDays-ஐ அதனால் எடுத்துக்கொள்ள முடியும்.

இப்போது, தேதியை ஒரு நாள் அதிகரிக்கும் inc-day என்ற செயல்பாட்டை எழுதுவோம்.

(defn inc-day [date-time]
  (.plusDays date-time 1))

அடுத்து, sequence-of-days என்ற செயல்பாட்டைப் பயன்படுத்தி தேதிகளின் லேசி சீக்வென்ஸை (lazy sequence) உருவாக்குவோம்.

(defn sequence-of-days []
  (iterate inc-day (LocalDate/now)))

நமது குறியீடு வேலை செய்கிறதா என்று பார்ப்போம். 7 நாட்களின் வரிசையை எடுத்துக்கொள்வோம்.

(take 7 (sequence-of-days))

வெளியீடு (Output)

(
  #object[java.time.LocalDate 0x59a25fd8 "2025-08-23"]
  #object[java.time.LocalDate 0x4829ecb8 "2025-08-24"]
  #object[java.time.LocalDate 0x65649752 "2025-08-25"]
  #object[java.time.LocalDate 0x192a07a6 "2025-08-26"]
  #object[java.time.LocalDate 0x523dd7c4 "2025-08-27"]
  #object[java.time.LocalDate 0x5d374cf0 "2025-08-28"]
  #object[java.time.LocalDate 0x61883365 "2025-08-29"]
)

இது வேலை செய்கிறது!

இப்போது அடுத்த n நாட்களைத் திருப்பித் தரும் next-n-days என்ற செயல்பாட்டை உருவாக்குவோம்.

(defn next-n-days)

இது n என்ற ஆர்கியுமெண்ட்டை ஏற்கிறது:

(defn next-n-days [n])

மேலும் இது இன்றைய தேதியிலிருந்து அடுத்த n நாட்களைத் திருப்பித் தருகிறது:

(defn next-n-days [n]
 (take n (sequence-of-days)))

ஆர்கியுமெண்ட் எதுவும் வழங்கப்படவில்லை என்றால்:

(defn next-n-days
  ([n]
   (take n (sequence-of-days)))
  ([]))

அது அடுத்த 7 நாட்களைத் திருப்பித் தர வேண்டும்:

(defn next-n-days
  ([n]
   (take n (sequence-of-days)))
  ([]
   (next-n-days 7)))

இதைச் சரிபார்ப்போம்.

(next-n-days)

வெளியீடு (Output)

(
  #object[java.time.LocalDate 0x5d4edd4c "2025-08-23"]
  #object[java.time.LocalDate 0x7f234335 "2025-08-24"]
  #object[java.time.LocalDate 0x1977020a "2025-08-25"]
  #object[java.time.LocalDate 0x483e3b96 "2025-08-26"]
  #object[java.time.LocalDate 0x63b13b60 "2025-08-27"]
  #object[java.time.LocalDate 0x66ef6c8 "2025-08-28"]
  #object[java.time.LocalDate 0x27a8c35d "2025-08-29"]
)

இப்போது அடுத்த 5 நாட்களை உருவாக்குவோம்:

(next-n-days 5)

வெளியீடு (Output)

(
  #object[java.time.LocalDate 0x5d184a50 "2025-08-23"]
  #object[java.time.LocalDate 0x71950d03 "2025-08-24"]
  #object[java.time.LocalDate 0x725327ff "2025-08-25"]
  #object[java.time.LocalDate 0x51dd6fcc "2025-08-26"]
  #object[java.time.LocalDate 0x4e08f56d "2025-08-27"]
)

இந்த பகுதியில், ஒரு ஜாவா கிளாஸை எவ்வாறு இம்போர்ட் செய்து அதன் செயல்பாடுகளை Clojure குறியீட்டில் பயன்படுத்துவது என்று பார்த்தோம். ஹோஸ்ட் மொழியின் சக்தியைப் பயன்படுத்தும் திறன் Clojure-ன் ஒரு வலிமையான அம்சமாகும். Clojure-ஐக் கற்றுக்கொள்வதன் மூலம், பல தசாப்தங்களாக மேம்படுத்தப்பட்ட, சரிபார்க்கப்பட்ட பல சுற்றுச்சூழல் அமைப்புகளை (இந்த புத்தகத்தில் அது ஜாவா சுற்றுச்சூழல் அமைப்பு) அணுகும் சக்தியை நீங்கள் பெறுகிறீர்கள்.

27. லீனைங்கன் மூலம் பிராஜெக்ட்களை உருவாக்குதல் (Projects With Leiningen)

லீனைங்கன் (Leiningen) என்பது Clojure பிராஜெக்ட்களை உருவாக்க அனுமதிக்கும் ஒரு கருவியாகும். இது டிபெண்டன்சிகளை (dependencies) பராமரிக்க, வெளி நூலகங்களைப் (external libraries) பயன்படுத்த மற்றும் உங்கள் பிராஜெக்டை ஒரு JAR கோப்பாக மாற்ற உதவுகிறது.

பிராஜெக்ட்களை உருவாக்க deps.edn என்ற மற்றொரு வழியும் உள்ளது; அதைப் பற்றி நான் நன்கு அறிந்த பிறகு எழுதுகிறேன்.

இந்த பகுதியில், நமக்கு "வணக்கம்" (Hello) கூறும் ஒரு சிறிய லீனைங்கன் பிராஜெக்டை உருவாக்குவோம்.

27.1. லீனைங்கன் பிராஜெக்டை உருவாக்குதல்

இந்தப் பகுதிக்கான குறியீட்டை இங்கே காணலாம் https://yu7.in/mGydAO
இந்தப் பகுதிக்கான வீடியோவை இங்கே காணலாம் https://yu7.in/clj-lein-proj

உங்கள் டெர்மினலைத் திறக்கவும். இந்த பிராஜெக்டிற்கு wish-me என்று பெயரிடுவோம், எனவே பயன்பாட்டை இவ்வாறு உருவாக்குவோம்:

$ lein new app wish-me

இப்போது wish-me என்ற பெயரில் ஒரு ஃபோல்டர் தோன்றும். அதன் உள்ளே சென்றால், பின்வரும் கட்டமைப்பைக் காண்பீர்கள்:

wish-me
├── CHANGELOG.md
├── LICENSE
├── README.md
├── doc
│   └── intro.md
├── pom.xml
├── project.clj
├── resources
├── src
│   └── wish_me
│       └── core.clj
├── target
└── test
    └── wish_me
        └── core_test.clj
ஒரு Clojure நிபுணர் இந்த கோப்புகள் ஒவ்வொன்றின் பொருளையும் விளக்குவார். ஆனால் நான் அந்த அளவு பெரிய ஆள் இல்லை; நான் ஒரு Clojure கற்றுக் கொள்பவன் மட்டுமே.

src/wish_me/core.clj என்ற கோப்பில் கவனம் செலுத்துங்கள்; அங்கு பின்வரும் குறியீட்டைப் பார்ப்பீர்கள்:

(ns wish-me.core
  (:gen-class))

(defn -main
  "I don't do a whole lot ... yet."
  [& args]
  (println "Hello, World!"))

இப்போது, டெர்மினலில் wish-me ஃபோல்டருக்குள் சென்று இவ்வாறு தட்டச்சு செய்யவும்:

$ lein run

உங்களுக்கு இந்த வெளியீடு கிடைக்கும்:

Hello, World!

நீங்கள் சரியாகக் கணித்திருந்தால், இந்த வெளியீடு src/wish_me/core.clj-ல் உள்ள (println "Hello, World!")-லிருந்து வந்தது. lein run என்பது -main செயல்பாடு src/wish_me/core.clj-ல் உள்ளது என்பதை எப்படி அறிந்திருக்கும் என்று நீங்கள் யோசித்தால், project.clj கோப்பைப் பாருங்கள்:

project.clj

(defproject wish-me "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0"
            :url "https://www.eclipse.org/legal/epl-2.0/"}
  :dependencies [[org.clojure/clojure "1.11.1"]]
  :main ^:skip-aot wish-me.core
  :target-path "target/%s"
  :profiles {:uberjar {:aot :all
                       :jvm-opts ["-Dclojure.compiler.direct-linking=true"]}})

:main ^:skip-aot wish-me.core என்ற வரி lein run எங்கிருந்து இயக்கத் தொடங்க வேண்டும் என்று சொல்கிறது.

27.2. ஆர்கியுமெண்டுகளை அச்சிடுதல் (Printing args)

இந்தப் பகுதிக்கான குறியீட்டை இங்கே காணலாம் https://yu7.in/kkXmN5

இப்போது, lein run-க்கு சில கமாண்ட்-லைன் ஆர்கியுமெண்டுகளை வழங்குவோம். நிரலை இவ்வாறு மாற்றியமைக்கவும்:

(ns wish-me.core
  (:gen-class))

(defn -main
  "I don't do a whole lot ... yet."
  [& args]
  (println args))

மேலே உள்ள குறியீட்டைச் செயல்படுத்த, src/wish_me/core.clj-ல் இருந்து (println "Hello, World!")-ஐ நீக்கிவிட்டு அதற்குப் பதிலாக (println args) என்பதைச் சேர்க்கவும். args என்ற மாறியானது வழங்கப்பட்ட கமாண்ட்-லைன் ஆர்கியுமெண்டுகளைக் கொண்டிருக்கும். இப்போது இயக்கவும்:

$ lein run Karthik

இது "Karthik" என்ற உறுப்பைக் கொண்ட ஒரு பட்டியலை அச்சிடும்:

(Karthik)

27.3. வணக்கம் கூறுதல் (Saying Hello)

இந்தப் பகுதிக்கான குறியீட்டை இங்கே காணலாம் https://yu7.in/qChinL

ஆர்கியுமெண்டுகளை அச்சிடுவதற்குப் பதிலாக, ஒரு நபரின் பெயர் வழங்கப்பட்டால் அவருக்கு வணக்கம் கூறுவோம். (println args) என்பதற்குப் பதிலாக (println (str "Hello " (first args) "!")) என்று மாற்றவும், அப்போது குறியீடு இவ்வாறு இருக்கும்:

(ns wish-me.core
  (:gen-class))

(defn -main
  "I don't do a whole lot ... yet."
  [& args]
  (println (str "Hello " (first args) "!")))

இப்போது இயக்கவும்:

$ lein run Karthik

உங்களுக்கு இந்த வெளியீடு கிடைக்கும்:

Hello Karthik!

27.4. வெளியீட்டை அலங்கரித்தல் (Decorating Output)

இந்தப் பகுதிக்கான குறியீட்டை இங்கே காணலாம் https://gitlab.com/clojure-book/wish-me/-/tree/decorating-output

உங்கள் முழு பிராஜெக்டையும் ஒரே ஒரு பெரிய கோப்பில் வைத்திருக்கக் கூடாது. அதற்குப் பதிலாக, உங்கள் நிரலை பல கோப்புகளாகப் பிரிக்கவும். இப்போது, src/wish_me/decorator.clj என்ற கோப்பை உருவாக்கி இந்தக் குறியீட்டைச் சேர்க்கவும்:

(ns wish-me.decorator)

(defn print-stars []
  (println "************************************"))

இப்போது இதை core.clj-ல் இணைப்போம்

(ns wish-me.core
  (:gen-class)
  (:require [wish-me.decorator :as d]))

(defn -main
  "I don't do a whole lot ... yet."
  [& args]
  (d/print-stars)
  (println (str "Hello " (first args) "!"))
  (d/print-stars))

மேலே உள்ள நிரலில், (:require [wish-me.decorator :as d]) என்ற வரி decorator நேம்ஸ்பேஸை d என்ற பெயரில் இணைக்கிறது. பின்னர் நாம் வாழ்த்துக்கு முன்னும் பின்னும் (d/print-stars)-ஐ அழைக்கிறோம்.

இப்போது இயக்கவும்:

$ lein run Karthik

வெளியீடு (Output)

************************************
Hello Karthik!
************************************

இப்போது உங்களுக்கு மேலே மற்றும் கீழே அலங்கார நட்சத்திரக் கோடுகளுடன் கூடிய ஒரு அழகான வாழ்த்து கிடைத்துள்ளது. ஒரு லீனைங்கன் பிராஜெக்டில் மற்ற கோப்புகளை இவ்வாறுதான் இணைப்போம்.

27.5. வெளி நூலகங்களை இணைத்தல் (Including External Libraries)

இந்தப் பகுதிக்கான குறியீட்டை இங்கே காணலாம் https://gitlab.com/clojure-book/wish-me/-/tree/external-library
இந்தப் பகுதிக்கான வீடியோவை இங்கே காணலாம் https://yu7.in/clj-ext-lib

இக்காலத்தில், நீங்கள் தனிமையாகக் குறியீடு எழுதுவது அரிது. லீனைங்கன் என்பதே நீங்கள் இணையத்திலிருந்து பதிவிறக்கிய ஒரு கருவிதான், அதன் மூலமாகவே நீங்கள் wish-me பிராஜெக்டை உருவாக்கினீர்கள். project.clj கோப்பைப் பார்த்தால், அதில் இவ்வாறு இருப்பதைக் காண்பீர்கள்:

:dependencies [[org.clojure/clojure "1.11.1"]]

இப்போது டிபெண்டன்சிகளில் [hiccup "2.0.0-RC2"] என்ற வரியைச் சேர்க்கவும், அது இவ்வாறு அமையும்:

:dependencies [[org.clojure/clojure "1.11.1"]
               [hiccup "2.0.0-RC2"]]

உங்கள் project.clj கோப்பு இவ்வாறு இருக்க வேண்டும்:

project.clj

(defproject wish-me "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0"
            :url "https://www.eclipse.org/legal/epl-2.0/"}
  :dependencies [[org.clojure/clojure "1.11.1"]
                 [hiccup "2.0.0-RC2"]]
  :main ^:skip-aot wish-me.core
  :target-path "target/%s"
  :profiles {:uberjar {:aot :all
                       :jvm-opts ["-Dclojure.compiler.direct-linking=true"]}})

Hiccup (https://clojars.org/hiccup) என்பது HTML-ஐ உருவாக்குவதற்கான ஒரு லைப்ரரி ஆகும். இப்போது, நீங்கள் lein run Someone --html என்று இயக்கினால், Hello Someone! என்று அச்சிடுவதற்குப் பதிலாக, உங்களுக்கு வாழ்த்து கூறும் HTML உள்ளடக்கத்தைக் கொண்ட hello.html என்ற கோப்பை உருவாக்குமாறு நமது நிரலை மாற்றுவோம்.

core.clj-ல் உள்ள குறியீட்டை இவ்வாறு மாற்றவும்:

(ns wish-me.core
  (:gen-class)
  (:require [wish-me.decorator :as d]
            [hiccup2.core :as h]))

(defn print-to-console [name]
  (d/print-stars)
  (println (str "Hello " name "!"))
  (d/print-stars))

(defn print-to-html [name]
  (spit "hello.html"
        (h/html [:html
                 [:body
                  [:h1 "Hello " name "!"]]])))

(defn -main
  "I don't do a whole lot ... yet."
  [& args]
  (if (= "--html" (second args))
    (print-to-html (first args))
    (print-to-console (first args))))

இப்போது இயக்கவும்:

$ lein run Karthik --html

இப்போது hello.html என்ற கோப்பு இருப்பதை நீங்கள் காண்பீர்கள். அதை உங்கள் உலாவியில் (browser) திறந்தால், இது போன்ற ஒரு பக்கத்தைக் காண்பீர்கள்:

htllo html

நாம் Hiccup லைப்ரரியை இணைத்து ஒரு வலைப்பக்கத்தை உருவாக்கப் பயன்படுத்தினோம். லீனைங்கன் இருப்பதால், இந்த செயல்முறை மிகவும் எளிதாக உள்ளது.

27.6. ஜார் (Jar)

இந்தப் பகுதிக்கான வீடியோவை இங்கே காணலாம் https://yu7.in/clj-jar

உங்கள் பயன்பாட்டை ஒரே ஒரு தனிப்பட்ட JAR கோப்பாக மாற்றலாம். இதை இயக்கவும்:

$ lein uberjar

target/default+uberjar ஃபோல்டரில் இரண்டு JAR கோப்புகள் உருவாக்கப்பட்டிருப்பதை நீங்கள் காண்பீர்கள்:

Compiling wish-me.core
Compiling wish-me.decorator
Created /Users/karthik/code/wish-me/target/default+uberjar/wish-me-0.1.0-SNAPSHOT.jar
Created /Users/karthik/code/wish-me/target/default+uberjar/wish-me-0.1.0-SNAPSHOT-standalone.jar

wish-me-0.1.0-SNAPSHOT.jar என்பது என்னவென்று எனக்குத் தெரியவில்லை, ஆனால் நீங்கள் wish-me-0.1.0-SNAPSHOT-standalone.jar-ஐ இவ்வாறு இயக்கலாம்:

$ java -jar target/default+uberjar/wish-me-0.1.0-SNAPSHOT-standalone.jar Karthik

இது வாழ்த்தை அச்சிடும்:

************************************
Hello Karthik!
************************************

மாற்றாக, --html கொடியை (flag) வழங்கினால் ஒரு HTML கோப்பை உருவாக்கும்:

$ java -jar target/default+uberjar/wish-me-0.1.0-SNAPSHOT-standalone.jar Karthik --html

27.7. REPL

இந்தப் பகுதிக்கான வீடியோவை இங்கே காணலாம் https://yu7.in/clj-test-lein
இந்தப் பகுதிக்கான குறியீட்டை இங்கே காணலாம் https://gitlab.com/clojure-book/wish-me/-/tree/repl

REPL-மூலம் மேம்பாடு செய்வது உங்கள் Clojure மேம்பாட்டு செயல்முறையை கணிசமாக வேகப்படுத்தும். முந்தைய பகுதிகளில், REPL-ஐத் தொடங்க lein repl-ஐப் பயன்படுத்தியிருக்கலாம், ஆனால் இது இப்போது ஒரு லீனைங்கன் பிராஜெக்ட் என்பதால், விஷயங்கள் இன்னும் எளிதாக உள்ளன.

Cmd+P (Mac) அல்லது Ctrl+P (Linux/Windows) அழுத்தி, பிராம்ப்டில் > jack in என்று தட்டச்சு செய்யவும்:

1

உங்களுக்கு இரண்டு விருப்பங்கள் தோன்றும்: ஒன்று "Start a Project REPL" மற்றும் மற்றொன்று "Copy the jack in command". "Start a Project REPL"-ஐத் தேர்ந்தெடுக்கவும். அடுத்து, Calva பல விருப்பங்களைக் காட்டும்; அதில் "Leiningen"-ஐத் தேர்ந்தெடுக்கவும்:

2

Calva கீழே காட்டியபடி :uberjar-ஐக் குறிப்பிடலாம்; உங்களுக்குத் தெரியவில்லை என்றால், Enter அழுத்தவும்.

3

பின்னர் REPL இவ்வாறு "jack in" ஆகும்:

4

இப்போது, src/wish_me/repl.clj என்ற கோப்பை உருவாக்கி இந்தக் குறியீட்டைச் சேர்க்கவும்:

repl.clj

(ns wish-me.repl)

(defn testing-repl []
  "Hello repl!")

REPL-ஐச் சோதிக்க, testing-repl-ன் மீது கர்சரை வைத்து Ctrl+Enter (Linux/Windows) அல்லது kbd[Option + Enter] (Mac) அழுத்தவும். செயல்பாடு REPL-ல் லோட் (load) ஆனதைக் காண்பீர்கள்.

5

இப்போது, REPL-ல் (testing-repl) என்று அழைத்தால், "Hello repl!" என்ற வெளியீடு கிடைக்கும்:

6

REPL-ஐ நிறுத்த, Cmd+P அல்லது Ctrl+P அழுத்தி பிராம்ப்டில் > jack out என்று தட்டச்சு செய்யவும். "Jack out" விருப்பத்தைத் தேர்ந்தெடுத்தால் REPL இவ்வாறு நின்றுவிடும்:

7

27.8. சோதனை செய்தல் (Testing)

இந்தப் பகுதிக்கான வீடியோவை இங்கே காணலாம் https://yu7.in/clj-test-lein

இப்போது, repl.clj-ல் உள்ள testing-repl செயல்பாட்டைச் சோதிப்பதன் மூலம் நமது குறியீட்டைச் சரிபார்ப்போம். src/wish_me/repl.clj-ல் உள்ள குறியீடு இதுதான்:

repl.clj

(ns wish-me.repl)

(defn testing-repl []
  "Hello repl!")

test/wish_me/repl_test.clj என்ற கோப்பை உருவாக்கி பின்வரும் குறியீட்டைச் சேர்க்கவும்:

repl_test.clj

(ns wish-me.repl-test
  (:require [clojure.test :refer :all]
            [wish-me.repl :refer :all]))

(deftest testing-repl-test
  (is (= "Hello repl!" (testing-repl))))

இந்தக் குறியீடு என்ன செய்கிறது என்று பார்ப்போம்:

(ns wish-me.repl-test)

இந்த வரி நாம் wish-me.repl-test நேம்ஸ்பேஸில் இருக்கிறோம் என்று அறிவிக்கிறது.

(ns wish-me.repl-test
  (:require [clojure.test :refer :all]))

(:require [clojure.test :refer :all]) என்ற வரி clojure.test-ல் உள்ள அனைத்து செயல்பாடுகளையும் தற்போதைய நேம்ஸ்பேஸில் இம்போர்ட் செய்கிறது. clojure.test-ல் எந்தெந்த செயல்பாடுகள் உள்ளன என்பதை அறிய https://clojuredocs.org/clojure.test/-ஐப் பார்வையிடலாம்.

நாம் wish-me.repl-ஐச் சோதிப்பதால், அந்த நேம்ஸ்பேஸிலும் உள்ள அனைத்து செயல்பாடுகளையும் குறிப்பிடுவோம்:

(ns wish-me.repl-test
  (:require [clojure.test :refer :all]
            [wish-me.repl :refer :all]))

இப்போது, repl-test.clj-ல் testing-repl-க்கான சோதனையைச் சேர்ப்போம்:

(ns wish-me.repl-test
  (:require [clojure.test :refer :all]
            [wish-me.repl :refer :all]))

(deftest testing-repl-test)

மேலே உள்ள குறியீடு deftest மேக்ரோவைப் பயன்படுத்தி testing-repl-test என்ற சோதனையை வரையறுக்கிறது. deftest என்பது clojure.test-ல் வரையறுக்கப்பட்டுள்ளது மற்றும் சோதனை வழக்குகளை (test cases) உருவாக்கப் பயன்படுகிறது.

இப்போது கீழே உள்ள குறியீட்டைப் பாருங்கள்:

(ns wish-me.repl-test
  (:require [clojure.test :refer :all]
            [wish-me.repl :refer :all]))

(deftest testing-repl-test
  (is (= "Hello repl!" (testing-repl))))

testing-repl-test செயல்பாட்டிற்குள் (is (= "Hello repl!" (testing-repl)))-ஐச் சேர்த்துள்ளோம். clojure.test-ல் உள்ள is செயல்பாடு ஒரு நிபந்தனை true ஆக இருக்கிறதா என்று உறுதிப்படுத்த (assert) பயன்படுத்தப்படுகிறது; இதைப் பற்றி இங்கே மேலும் அறியலாம்: https://clojuredocs.org/clojure.test/is/.

testing-repl என்பது "Hello repl!"-க்கு சமமா என்று சரிபார்க்க (= "Hello repl!" (testing-repl))-ஐப் பயன்படுத்துகிறோம். அவ்வாறு இருந்தால், சோதனை வெற்றி பெறும்; இல்லையெனில் தோல்வியடையும்.

இப்போது அதைச் சோதிப்போம். உங்கள் டெர்மினலில் பின்வரும் கட்டளையை இயக்கவும்:

$ lein test :only wish-me.repl-test

lein test லீனைங்கன் பிராஜெக்டில் உள்ள சோதனைகளை இயக்கும். wish-me.repl-test நேம்ஸ்பேஸை மட்டும் இயக்க, :only விருப்பத்தைப் பயன்படுத்துகிறோம்:

வெளியீடு ஒரு சோதனை மற்றும் ஒரு அஸர்ஷன் (assertion) இயங்கி, தோல்விகளோ அல்லது பிழைகளோ இல்லை என்பதைக் காட்டும்:

lein test wish-me.repl-test

Ran 1 tests containing 1 assertions.
0 failures, 0 errors.

பிராஜெக்டிலுள்ள அனைத்து சோதனைகளையும் இயக்க:

$ lein test

இந்த வழக்கில், ஒரு சோதனை தோல்வியடைகிறது. நீங்கள் core_test.clj-ஐப் பரிசோதித்து அந்தப் பிரச்சினையைச் சரிசெய்ய முயற்சி செய்யலாம்.

lein test wish-me.core-test

lein test :only wish-me.core-test/a-test

FAIL in (a-test) (core_test.clj:7)
FIXME, I fail.
expected: (= 0 1)
  actual: (not (= 0 1))

lein test wish-me.repl-test

Ran 2 tests containing 2 assertions.
1 failures, 0 errors.
Subprocess failed (exit code: 1)

ஒரு நேம்ஸ்பேஸிற்குள் குறிப்பிட்ட சோதனையை, உதாரணமாக testing-repl-test-ஐ இயக்க பின்வரும் கட்டளையைப் பயன்படுத்தவும்:

$ lein test :only wish-me.repl-test/testing-repl-test

கீழே காட்டியபடி, சோதனை இயங்கி அஸர்ஷன் வெற்றி பெற்றது:

lein test wish-me.repl-test

Ran 1 tests containing 1 assertions.
0 failures, 0 errors.

28. பிரைவேட் செயல்பாடுகள் (Private Functions)

குறிப்பு: இந்தப் பகுதிக்கான வீடியோவை இங்கே காணலாம்: https://yu7.in/private-functions-in-clojure

குறிப்பு: இந்தப் பகுதிக்கான குறியீட்டை இங்கே பெறவும்: https://gitlab.com/clojure-book/private_functions

ஒரு Clojure நேம்ஸ்பேஸை (namespace) உருவாக்கும்போது, அதன் அனைத்து செயல்பாடுகளையும் வெளி உலகிற்குத் தெரியப்படுத்த வேண்டிய அவசியம் எப்போதும் இருக்காது. அவசியமானவற்றை மட்டும் வெளிப்படுத்துவது போதுமானது. சிக்கல்களைக் குறைக்கவும், குறியீட்டை எளிதாக நிர்வகிக்கவும் பிரைவேட் செயல்பாடுகளைப் (private functions) பயன்படுத்தலாம். இந்த செயல்பாடுகளை பிரைவேட் என்று அறிவிப்பதன் மூலம் அவற்றை மறைக்க முடியும். இந்தப் பகுதியில், பிரைவேட் செயல்பாடுகளை எவ்வாறு வரையறுப்பது மற்றும் அவற்றை எவ்வாறு பயன்படுத்துவது என்று பார்ப்போம்.

முதலில், private_functions என்ற லீனைங்கன் பிராஜெக்டை உருவாக்குவோம்.

$ lein new private_functions

இப்போது src/private_functions/private_and_public_functions.clj என்ற கோப்பை உருவாக்கி அதில் பின்வரும் உள்ளடக்கத்தைச் சேர்க்கவும்:

src/private_functions/private_and_public_functions.clj
(ns private-functions.private-and-public-functions)

(defn public-fn []
  "A public function")

இப்போது, src/private_functions/core.clj என்ற கோப்பை உருவாக்கி அதில் பின்வரும் உள்ளடக்கத்தைச் சேர்க்கவும்:

src/private_functions/core.clj
(ns private-functions.core
  (:require [private-functions.private-and-public-functions :as f]))

(f/public-fn)

இங்கே private-and-public-functions நேம்ஸ்பேஸை f என்ற பெயரில் இணைத்துள்ளோம் என்பதைக் கவனியுங்கள். பின்னர் (f/public-fn) மூலம் பப்ளிக் செயல்பாட்டை அழைக்கிறோம். பிராஜெக்டை jack-in செய்து (f/public-fn) என்று இயக்கினால், உங்களுக்கு பின்வரும் வெளியீடு கிடைக்கும்:

"A public function"

இப்போது private-and-public-functions.clj-ல் ஒரு பிரைவேட் செயல்பாட்டைச் சேர்ப்போம். கீழே உள்ள குறியீட்டைப் பாருங்கள்:

src/private_functions/private_and_public_functions.clj
(ns private-functions.private-and-public-functions)

(defn public-fn []
  "A public function")

(defn- private-fn []
  "A private function")

defn-க்கு பதிலாக defn- பயன்படுத்தப்பட்டிருப்பதைக் கவனியுங்கள்:

src/private_functions/private_and_public_functions.clj
(defn- private-// a mistake here, tool is write_file
L31-L40 in private_functions.adoc:
(f/public-// a mistake here, tool is write_file
L41 in core.clj:
(f/public-fn)
(f/private-fn) ;; This will not work; you cannot access a private function from another namespace.

29. Calva மற்றும் nREPL மூலம் லீனைங்கன் பிராஜெக்டில் டீபகிங் செய்தல் (Debugging in Leiningen project with Calva and nREPL)

குறிப்பு: அதிகாரப்பூர்வ Calva டீபக்கர் ஆவணம் https://calva.io/debugger/

குறிப்பு: இந்தப் பகுதிக்கான குறியீட்டை இங்கே காணலாம் https://gitlab.com/clojure-book/projects/dbg_and_break

Clojure குறியீட்டை படிப்படியாக டீபக் செய்யவும், பிரேக் பாயிண்ட்களை (breakpoints) அமைக்கவும் மற்றும் மதிப்புகளை ஆய்வு செய்யவும் அனுமதிக்கும் nREPL எனப்படும் ஒரு சிறந்த லைப்ரரி உள்ளது. இது குறியீட்டின் செயல்பாட்டை நீங்கள் சிறப்பாகப் புரிந்துகொள்ள உதவுகிறது.

தொடங்குவதற்கு, dbg_and_break என்ற லீனைங்கன் பிராஜெக்டை உருவாக்குவோம்.

$ lein new dbg_and_break

இப்போது, பிராஜெக்ட் டைரக்டரிக்கு செல்லுங்கள்:

$ cd dbg_and_break

project.clj கோப்பைத் திறக்கவும், அதில் இது போன்ற ஒன்றை நீங்கள் காண்பீர்கள்:

(defproject dbg_and_break "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0"
            :url "https://www.eclipse.org/legal/epl-2.0/"}
  :dependencies [[org.clojure/clojure "1.12.4"]]
  :repl-options {:init-ns dbg-and-break.core})

டிபெண்டன்சிகளைக் கவனியுங்கள்:

:dependencies [[org.clojure/clojure "1.12.4"]]

இப்போது அற்புதமான nREPL லைப்ரரியைச் சேர்ப்போம். டிபெண்டன்சிகள் இப்போது இவ்வாறு இருக்க வேண்டும்:

:dependencies [[org.clojure/clojure "1.12.4"]
                [nrepl "1.7.0"]]

உங்கள் project.clj கோப்பு இப்போது இவ்வாறு இருக்க வேண்டும்:

(defproject dbg_and_break "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0"
            :url "https://www.eclipse.org/legal/epl-2.0/"}
  :dependencies [[org.clojure/clojure "1.12.4"]
                 [nrepl "1.7.0"]]
  :repl-options {:init-ns dbg-and-break.core})

src/dbg_and_break/loop.clj என்ற கோப்பை உருவாக்கி அதில் பின்வரும் குறியீட்டைச் சேர்க்கவும்:

(ns dbg-and-break.loop)

(loop [i 1]
  (when (<= i 5)
    (println i)
    (recur (inc i))))

Jack in செய்து குறியீட்டை இயக்கவும்; உங்களுக்கு இவ்வாறு வெளியீடு கிடைக்க வேண்டும்:

வெளியீடு (Output)

; 1
; 2
; 3
; 4
; 5
nil

எல்லாம் சரியாக வேலை செய்தால், நாம் தொடரலாம்.

29.1. #dbg

VS Code-ல், ctrl+shift+p அல்லது command+shift+p அழுத்தவும், அல்லது மேலிருக்கும் தேடல் பட்டியில் > என்று தட்டச்சு செய்து பின்னர் instrument என்று தட்டச்சு செய்யவும். உங்களுக்கு இது போன்ற ஒரு விருப்பம் தோன்றும்:

01 instrument top level form

Instrument Top Level Form for Debug என்பதைத் தேர்ந்தெடுக்கவும். கீழே உள்ள படத்தின் வலது பக்கத்தில், உங்கள் குறியீட்டின் மேலே #dbg தோன்றியிருப்பதை கவனியுங்கள். இந்த #dbg ஆனது project.clj-ல் சேர்க்கப்பட்ட nREPL லைப்ரரிக்கு நீங்கள் டீபக் மோடு-ல் (debug mode) இருக்கிறீர்கள் என்று சொல்கிறது. இந்த செயல்முறை இன்ஸ்ட்ருமென்டேஷன் (instrumentation) என்று அழைக்கப்படுகிறது.

02 form instrumented

உங்கள் எடிட்டரின் மேலே, பின்வரும் டீபக்கர் கட்டுப்பாடுகளைக் (debugger controls) காண்பீர்கள்:

debug controls

பட்டன்கள் பின்வருமாறு:

debugging controls numbered
  1. Continue (தொடரவும்)

  2. Step Over (தவிர் மற்றும் அடுத்த வரிக்குச் செல்லவும்)

  3. Step Into (உள்ளே செல்லவும்)

  4. Step Out (வெளியே வரவும்)

  5. Restart (மீண்டும் தொடங்கவும்)

  6. Disconnect (தொடர்பை துண்டிக்கவும்)

இங்கு மிகவும் பொதுவாகப் பயன்படுத்தப்படும் மூன்று பட்டன்களைப் பற்றிப் பார்ப்போம்.

நீங்கள் ஃபார்மை (form) இன்ஸ்ட்ருமென்ட் செய்தவுடன், (loop …​) இயங்கத் தொடங்கி (when (⇐ i 5)) நிபந்தனையில் நிறுத்திவிடும், அது கீழே காட்டியுள்ளது:

03 stepping though condition

உங்கள் எடிட்டரில் (⇐ i 5) என்பது true என்று டீபக்கர் காட்டுவதை நீங்கள் காணலாம். இடதுபுறத்தில், i-ன் தற்போதைய மதிப்பு 1 என்று Calva காட்டுகிறது. இது ஒரு மிகச்சிறந்த டீபகிங் அனுபவத்தை வழங்குகிறது.

இப்போது Step Into பட்டனை (கீழ்நோக்கிய அம்புக்குறியைக் கொண்ட பட்டன்) அழுத்தவும். நீங்கள் பின்வருவனவற்றைக் காண்பீர்கள்:

04 i value in println

மேலே உள்ள படத்தில் பார்த்தபடி, i-ன் மதிப்பு இன்னும் 1-ஆகவே உள்ளது, ஆனால் டீபக்கர் (println i) வரியில் நிறுத்தியுள்ளது. மீண்டும் Step Into அழுத்தவும்; println என்பது nil-ஐத் திருப்பித் தந்தது மற்றும் i-ன் மதிப்பு வலதுபுறத்தில் உள்ள Calva REPL வெளியீட்டில் அச்சிடப்பட்டது என்பதைக் காண்பீர்கள் (கீழே உள்ள படத்தைப் பார்க்கவும்).

05 i gets printed

மீண்டும் Step Into அழுத்தவும், டீபக்கர் (recur (inc i)) வரியில் நிற்கும், அப்போது i-ன் மதிப்பு இன்னும் 1-ஆகவே இருக்கும்.

06 i in inc

இன்னொரு முறை Step Into அழுத்தவும், அப்போது (inc i) என்பது 2-ஐத் திருப்பித் தருவதையும், i-ன் மதிப்பு 2 ஆவதையும் காண்பீர்கள்:

07 i gets incremented

மீண்டும் Step Into அழுத்தவும், recur இயக்கப்பட்ட பிறகு, நிரல் மீண்டும் (when (⇐ i 5)) வரியில் நிற்கும், இப்போது i-ன் மதிப்பு 2.

இவ்வாறு உங்கள் குறியீட்டைப் படிப்படியாக இயக்கி, தரவு ஓட்டத்தைப் புரிந்துகொண்டு உங்கள் தர்க்கத்தில் (logic) உள்ள பிழைகளைத் திருத்தலாம்.
08 at the top of loop

Step Into அழுத்தப் பதிலாக, நீங்கள் Continue பட்டனை அழுத்தலாம். நிரல் மீண்டும் (when (⇐ i 5)) வரியில் நிற்கும், இப்போது i-ன் மதிப்பு 3 ஆக இருக்கும்.

09 pressing continue

நிரல் (when (⇐ i 5)) வரியில் நின்று i-ன் மதிப்பு 6-ஐ அடையும் வரை Continue பட்டனைத் தொடர்ந்து அழுத்தவும்.

10 i is 6 condition fails

கடைசியாக ஒரு முறை Continue அழுத்தவும், அப்போது loop என்பது nil-ஐத் திருப்பிக் கொடுப்பதைக் காண்பீர்கள்.

11 we finished running

லீனைங்கன் பிராஜெக்டில் nREPL மற்றும் Calva-வைப் பயன்படுத்தி உங்கள் குறியீட்டை டீபக் செய்வது இப்படித்தான்.

29.2. வெளியேறுதல் (Quitting)

ஒரு டீபகிங் செஷனை நிறுத்த, மீண்டும் ஃபார்மை இன்ஸ்ட்ருமென்ட் செய்து i மதிப்பு 6-க்குக் குறைவாக வரும் வரை Continue அழுத்தவும். பின்னர், Disconnect பட்டனை அழுத்தவும்:

12 quitting

நிரல் முடிவடையும். இந்த வழக்கில், 4 வரையிலான மதிப்புகள் மட்டுமே அச்சிடப்பட்டன. டீபகிங் முடிந்து செயல்முறையை நிறுத்த விரும்பும் போது Disconnect பட்டன் மிகவும் பயனுள்ளதாக இருக்கும்.

29.3. வெளிப்படையான #dbg பயன்பாடு (Using explicit #dbg)

VS Code மெனுவைப் பயன்படுத்தி ஃபார்மை இன்ஸ்ட்ருமென்ட் செய்வதற்குப் பதிலாக, உங்கள் குறியீட்டில் ஃபார்முக்கு முன்னால் நேரடியாக #dbg-ஐ வைக்கலாம்:

(ns dbg-and-break.explicit-debug)

#dbg
(loop [i 1]
  (when (<= i 5)
     (println i)
    (recur (inc i))))

டீபகிங் அனுபவம் முந்தைய முறைக்கு இணையாகவே இருக்கும். ஃபார்முக்கு முன்னால் #dbg-ஐ சேர்த்த பிறகு, கர்சரை ஃபார்முக்குள் வைத்து alt+enter அல்லது option+enter அழுத்தினால் டீபக்கர் தொடங்கும்.

29.4. #break

ஆரம்பத்தில் கீழே உள்ள நிரலில் #break-ஐப் பயன்படுத்த முயற்சி செய்தேன், ஆனால் அது வேலை செய்யவில்லை. ஒரு AI-யிடம் ஆலோசித்த பிறகு, பின்வரும் உதாரணத்தைக் கண்டறிந்தேன்:

(ns dbg-and-break.loop)

(loop [i 1]
  (when (<= i 5)
    #dbg ^{:break/when (= i 3)}
    (println i)
    (recur (inc i))))

#dbg ^{:break/when (= i 3)} என்ற வரியில், ^{:break/when (= i 3)} என்ற மெட்டாடேட்டா, i-ன் மதிப்பு 3 ஆக இருக்கும்போது மட்டும் பிரேக் செய்யும்படி டீபக்கருக்குச் சொல்கிறது, அந்தப் புள்ளியில் #dbg இன்ஸ்ட்ருமென்டேஷன் செயல்படும்.

Jack in செய்து, கர்சரை ஃபார்முக்குள் வைத்து alt+enter அல்லது option+enter அழுத்தவும். நீங்கள் பின்வருவனவற்றைக் காண்பீர்கள்:

01 break when

படத்தில் காட்டியபடி, 1 மற்றும் 2 அச்சிடப்படுகின்றன, மற்றும் i ஆனது 3 ஆக இருக்கும்போது நிரல் நிறுத்தப்படுகிறது. Continue பட்டனை அழுத்தினால், i ஆனது 6 ஆகும் வரை இயங்கி, 1 முதல் 5 வரையிலான அனைத்து மதிப்புகளையும் அச்சிடும்.

02 break when

நிரல் பிரேக் ஆகி #dbg செயல்படும்போது, நீங்கள் i-ன் மதிப்பை ஆய்வு செய்து பல்வேறு டீபகிங் பணிகளைச் செய்யலாம். இது ஒரு எளிய உதாரணம் என்றாலும், பல மதிப்புகளைக் கையாளும் சிக்கலான நிரல்களில் இத்தகைய நிபந்தனை சார்ந்த பிரேக்குகள் (conditional breaks) மிகவும் பயனுள்ளதாக இருக்கும்.

break-ன் பயன்பாட்டை மேலும் விளக்க, இந்த நிரலைப் பாருங்கள்:

(ns dbg-and-break.apply-discount)

(defn apply-discount [order]
  (let [price  (:price order)
        qty    (:qty order)
        total  (* price qty)
        ;; Bug: discount applies to price, not total
        discount (* price 0.1)]
    #break
     (- total discount)))

(defn process-orders [orders]
  (map apply-discount orders))

(process-orders [{:price 100 :qty 3}
                 {:price 50  :qty 5}])

Jack in செய்து, கர்சரை apply-discount-ல் வைத்து alt+enter அல்லது option+enter அழுத்தி அதை REPL-ல் லோட் செய்யவும். process-orders-க்கும் அதையே செய்யவும்.

இப்போது, (process-orders …​) ஃபார்மில் கர்சரை வைத்து alt+enter அல்லது option+enter அழுத்தவும். நீங்கள் பின்வருவனவற்றைக் காண்பீர்கள்:

03 break

டீபக்கர் (- total discount) வரியில் பிரேக் ஆகிறது. நீங்கள் இடதுபுறத்தில் total, discount, price, மற்றும் qty மதிப்புகள் மற்றும் order மேப்பைக் காணலாம். (- total discount) வரி சிவப்பு நிறத்தில் ஹைலைட் செய்யப்பட்டுள்ளது, அது 290-ஐத் திருப்பியது என்பதைக் காட்டுகிறது.

Continue பட்டனை அழுத்தவும், நிரல் மீண்டும் நிறுத்தப்படும், மற்றும் (- total discount)-ன் மதிப்பு 290-லிருந்து 245-ஆக மாறும்.

04 break

மீண்டும் Continue அழுத்தவும், நிரல் அதன் செயல்பாட்டை நிறைவு செய்யும்:

05 break
(process-orders [{:price 100 :qty 3}
                 {:price 50  :qty 5}])

இதன் வெளியீடு (290.0 245.0) என்ற பட்டியலாக இருக்கும். அதை மேலே உள்ள படத்தின் வலதுபுறத்தில் உள்ள REPL வெளியீட்டில் காணலாம்.

30. கோப்பு செயல்பாடுகள் (File Operations)

இந்த பிரிவில், Clojure-இல் கோப்புகளை (files) எவ்வாறு கையாள்வது என்று பார்ப்போம். மேலும் சில பிரபலமான கோப்பு வடிவங்களையும் (file formats), அவற்றை எவ்வாறு கையாள்வது என்பதையும் பார்ப்போம்.

30.1. உரை (Text)

உரை கோப்புகள் (Text files) வெறும் உரை, எழுத்துக்கள் மற்றும் எண்களை மட்டுமே கொண்டிருக்கும். இவற்றை கையாள்வது மிகவும் எளிதானது. ஒரு காலியான Clojure கோப்பை உருவாக்கி, அதற்கு text_files.clj என்று பெயரிட்டு, பின்வருவனவற்றை இயக்கிப் பாருங்கள்:

(spit "hello.txt" "Hello World!")

Hello World! என்ற உள்ளடக்கத்துடன் hello.txt என்ற கோப்பு உருவாக்கப்படும்.

spit சார்பு (function) இரண்டு ஆர்கியுமெண்ட்களை (arguments) எடுத்துக்கொள்ளும்: முதலாவது கோப்பின் பெயர், இரண்டாவது கோப்பில் எழுதப்பட வேண்டிய உள்ளடக்கம். இதை இயக்கியவுடன், உள்ளடக்கத்துடன் கூடிய ஒரு கோப்பு உங்களுக்கு கிடைக்கும். அவ்வளவுதான், இது மிகவும் எளிமையானது.

கோப்பில் எழுதுவதற்கு spit பயன்படுத்தப்படுகிறது, கோப்பிலிருந்து படிக்க slurp பயன்படுத்தப்படுகிறது. இப்போது hello.txt-ன் உள்ளடக்கத்தைப் படித்து அதை அச்சிடுவோம்:

(println (slurp "hello.txt"))

வெளியீடு (Output)

Hello World!

(slurp "hello.txt") என்பது hello.txt-ன் உள்ளடக்கத்தைப் படித்து அதை ஒரு ஸ்ட்ரிங்காக (string) வழங்கும், println அதை மேலே காட்டியபடி அச்சிடும்.

இப்போது கோப்பில் ஒரு கூடுதல் வரியைச் சேர்க்க விரும்பினால், கீழே காட்டியபடி :append true என்பதைப் பயன்படுத்தலாம்:

(spit "hello.txt" "\nHello Mars!" :append true)

(println (slurp "hello.txt"))

வெளியீடு (Output)

Hello World!
Hello Mars!

முழு கோப்பும் அழிக்கப்படுவதற்குப் பதிலாக, hello.txt கோப்பின் இறுதியில் "\nHello Mars!" என்பது சேர்க்கப்படுகிறது.

இப்போது hello.txt கோப்பை நீக்குவோம்:

(require '[clojure.java.io :as io])

(io/delete-file "hello.txt")

ஒரு கோப்பை நீக்குவதற்கு, (require '[clojure.java.io :as io]) மூலம் clojure.java.io லைப்ரரியை இணைக்க வேண்டும், பின்னர் கோப்பின் பெயரை ஆர்கியுமெண்டாகக் கொண்டு io/delete-file சார்பை அழைக்க வேண்டும்: (io/delete-file "hello.txt").

hello.txt கோப்பு நீக்கப்படும்.

இந்த பிரிவிற்கான முழு மூலக் குறியீடு (source code) கீழே கொடுக்கப்பட்டுள்ளது, மேலும் நீங்கள் அதை இங்கிருந்து பெறலாம் https://gitlab.com/clojure-book/code/-/raw/master/text_files.clj

;; text_files.clj

(spit "hello.txt" "Hello World!")

(println (slurp "hello.txt"))

;; கோப்புகளில் உள்ளடக்கத்தைச் சேர்த்தல் (appending content to files)

(spit "hello.txt" "\nHello Mars!" :append true)

(println (slurp "hello.txt"))

;; கோப்புகளை நீக்குதல் (deleting files)

(require '[clojure.java.io :as io])

(io/delete-file "hello.txt")

30.2. edn

குறிப்பு: இதன் மூலக் குறியீட்டை (source code) இங்கே காணலாம் https://gitlab.com/clojure-book/code/-/raw/master/edn_file.clj

குறிப்பு: இந்தப் பிரிவிற்கான வீடியோவை இங்கே பார்க்கலாம் https://yu7.in/clj-edn

EDN, அல்லது Extensible Data Notation https://github.com/edn-format/edn என்பது Clojure தரவைச் சேமிக்கவும் பரிமாறவும் பயன்படுத்தும் விருப்பமான வழியாகும். EDN-க்கான ஆதரவு Clojure-இல் உள்ளமைக்கப்பட்டுள்ளது. EDN-ஐ எப்படிக் கையாள்வது என்று தெரிந்துகொள்ள, கீழே காட்டியபடி ஒரு எளிய EDN கோப்பை உருவாக்குவோம்:

spell_numbers.edn
{1 "One" 2 "Two" 3 "Three"}

இப்போது இந்த EDN கோப்பைப் படிக்கவும் எழுதவும் ஒரு Clojure கோப்பை உருவாக்குவோம், அதற்கு edn_file.clj என்று பெயரிடுவோம். முதலில், Clojure EDN லைப்ரரியை இணைப்போம்:

(require '[clojure.edn :as edn])

வெளியீடு (Output)

nil

இப்போது spell_numbers.edn கோப்பைப் படித்து அதை spell-numbers என்ற வேரியபிளில் சேமிப்போம்:

(def spell-numbers
  (edn/read-string (slurp "spell_numbers.edn")))

வெளியீடு (Output)

#'user/spell-numbers

இப்போது spell-numbers வேரியபிளை அச்சிடுவோம்:

(println spell-numbers)

வெளியீடு (Output)

{1 One, 2 Two, 3 Three}

இப்போது spell-numbers மேப்பிற்கு (map) ஒரு புதிய மதிப்பைச் சேர்ப்போம்; நாம் 4 என்ற கீயுடன் (key) "Four" என்ற மதிப்பைச் சேர்ப்போம்:

(assoc spell-numbers 4 "Four")

இதை ஒரு ஸ்ட்ரிங்காக மாற்றுவோம்:

(prn-str (assoc spell-numbers 4 "Four"))

இதை spell_numbers.edn கோப்பில் எழுதுவோம்:

(spit "spell_numbers.edn" (prn-str (assoc spell-numbers 4 "Four")))

இப்போது spell_numbers.edn கோப்பின் உள்ளடக்கத்தைப் பார்ப்போம்:

{1 "One", 2 "Two", 3 "Three", 4 "Four"}

நீங்கள் காண்பது போல 4 "Four" மேப்பிற்குச் சேர்க்கப்பட்டுள்ளது.

இப்போது spell_numbers.edn கோப்பைப் படித்து அதை spell-numbers-appended என்ற வேரியபிளில் சேமிப்போம்:

(def spell-numbers-appended
  (edn/read-string (slurp "spell_numbers.edn")))

வெளியீடு (Output)

#'user/spell-numbers-appended

இப்போது spell-numbers-appended வேரியபிளை அச்சிடுவோம்:

(println spell-numbers-appended)

வெளியீடு (Output)

{1 One, 2 Two, 3 Three, 4 Four}

இப்போது spell_numbers.edn கோப்பை spell-numbers வேரியபிளைக் கொண்டு மீண்டும் எழுதுவோம்:

(spit "spell_numbers.edn" (prn-str spell-numbers))

வெளியீடு (Output)

nil

இப்போது நாம் மீண்டும் பழைய spell_numbers.edn கோப்பிற்கு வந்துவிட்டோம், அது கீழே காட்டியபடி இருக்கும்:

{1 "One", 2 "Two", 3 "Three"}

edn_file.clj-ன் முழு மூலக் குறியீட்டை https://gitlab.com/clojure-book/code/-/raw/master/edn_file.clj-இல் பெறலாம்.

;; edn_file.clj

(require '[clojure.edn :as edn])

(def spell-numbers
  (edn/read-string (slurp "spell_numbers.edn")))

(println spell-numbers)

(spit "spell_numbers.edn" (prn-str (assoc spell-numbers 4 "Four")))

(def spell-numbers-appended
  (edn/read-string (slurp "spell_numbers.edn")))

(println spell-numbers-appended)

(spit "spell_numbers.edn" (prn-str spell-numbers))

30.3. CSV

குறிப்பு: இதன் பிராஜெக்ட் மூலக் குறியீட்டை இங்கே காணலாம் https://gitlab.com/clojure-book/file-ops/-/tree/csv_ops

குறிப்பு: இந்தப் பிரிவிற்கான வீடியோவை இங்கே பார்க்கலாம் https://yu7.in/clj-csv

CSV என்பது Comma-Separated Values என்பதன் சுருக்கமாகும். இது அட்டவணை தரவுகளை (tabular data) சேமிக்கப் பயன்படுத்தப்படும் மிகவும் பொதுவான கோப்பு வடிவமாகும். Clojure-இல், CSV கோப்புகளைப் படிக்கவும் எழுதவும் data.csv லைப்ரரியைப் பயன்படுத்துகிறோம். அந்த லைப்ரரியை இங்கே காணலாம்: https://github.com/clojure/data.csv

முதலில் file-ops என்ற பெயரில் ஒரு Leiningen பிராஜெக்டை உருவாக்குவோம்:

$ lein new app file-ops

project.clj கோப்பில்:

project.clj
(defproject file-ops "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0"
            :url "https://www.eclipse.org/legal/epl-2.0/"}
  :dependencies [[org.clojure/clojure "1.11.1"]
                 [org.clojure/data.csv "1.1.0"]]
  :repl-options {:init-ns file-ops.core})

மேலே காட்டியபடி :dependencies வெக்டரில் [org.clojure/data.csv "1.1.0"] என்ற வரியைச் சேர்க்கவும்.

இப்போது பின்வரும் உள்ளடக்கத்துடன் in-file.csv என்ற கோப்பை உருவாக்குவோம்:

in-file.csv
name, profession
Karthikeyan, Clojure Programmer
Pari, Data Engineer

இப்போது பிராஜெக்டில் src/file_ops/csv_ops.clj என்ற கோப்பை உருவாக்கி இந்த உள்ளடக்கத்தைச் சேர்க்கவும்:

(ns file-ops.csv-ops
  (:require [clojure.data.csv :as csv]
            [clojure.java.io :as io]))

clojure.data.csv என்பது CSV உள்ளடக்கத்தைக் கையாள்வதற்குப் பயன்படுத்தப்படுகிறது மற்றும் அது csv என இறக்குமதி செய்யப்பட்டுள்ளது. clojure.java.io என்பது கோப்பு I/O-விற்குப் பயன்படுத்தப்படுகிறது மற்றும் அது io என இறக்குமதி செய்யப்பட்டுள்ளது.

இப்போது ஜாக்-இன் (jack-in) செய்து இந்த சார்பைச் சேர்க்கவும்:

(defn csv-reader [filename]
  (with-open [reader (io/reader filename)]
    (doall
     (csv/read-csv reader))))

இது எவ்வாறு செயல்படுகிறது என்பதைப் புரிந்துகொள்ள, இதை உடைத்து பார்ப்போம். முதலில், csv-reader என்ற பெயரில் ஒரு சார்பை வரையறுக்கிறோம்:

(defn csv-reader [])

இது filename என்ற ஆர்கியுமெண்ட்டை ஏற்கும் படி மாற்றுகிறோம்:

(defn csv-reader [filename])

கோப்பைத் திறக்கிறோம்:

(defn csv-reader [filename]
  (with-open [reader (io/reader filename)]))

csv/read-csv ஐப் பயன்படுத்தி கோப்பைப் படிக்கிறோம்:

(defn csv-reader [filename]
  (with-open [reader (io/reader filename)]
     (csv/read-csv reader)))

read-csv என்பது லேஸி (lazy) என்பதால், with-open மூலம் ரீடர் மூடப்படுவதற்கு முன்பே கோப்பு முழுமையாகப் படிக்கப்பட்டிருப்பதை உறுதி செய்ய அதை doall மூலம் சுற்றி வைக்கிறோம்:

(defn csv-reader [filename]
  (with-open [reader (io/reader filename)]
    (doall
     (csv/read-csv reader))))

Alt+Enter அல்லது Option+Enter அழுத்தி இதை REPL-இல் லோட் செய்யவும். இப்போது நமது CSV-ஐப் படிக்கலாம்:

(csv-reader "in-file.csv")

வெளியீடு (Output):

(["name" "profession"] ["Karthikeyan" "Clojure Programmer"] ["Pari" "Data Engineer"])

நீங்கள் காண்பது போல, CSV-ஐ வெறும் உரை கோப்பாகப் படிப்பதற்குப் பதிலாக, நாம் வெக்டர்களின் வரிசையாக (sequence of vectors)ப் பெறுகிறோம், இது கையாள்வதற்கு மிகவும் எளிது.

இதேபோல், csv/write-csv சார்பைப் பயன்படுத்தி CSV கோப்புகளை எழுதலாம். கீழே ஒரு ரைட்டர் (writer) சார்பை வரையறுப்போம்:

(defn csv-writer [filename data]
  (with-open [writer (io/writer filename)]
    (doall
     (csv/write-csv writer data))))

இப்போது ஒரு CSV-ஐ எழுதுவோம்:

(csv-writer "out-file.csv"
            [["name" "profession"]
             ["Karthikeyan" "Clojure Programmer"]
             ["Pari" "Data Engineer"]
             ["Bill Gates" "Shackles you up"]
             ["Richard M Stallman" "Unshackles you"]])

out-file.csv-ஐப் பார்த்தால், நமக்குக் கிடைப்பது:

out-file.csv
name,profession
Karthikeyan,Clojure Programmer
Pari,Data Engineer
Bill Gates,Shackles you up
Richard M Stallman,Unshackles you

Clojure புரோகிராமர்கள் பொதுவாக வெக்டர்களின் வரிசையை விட மேப்களைப் (maps) பயன்படுத்துவதில் அதிக வசதியாக உணர்கிறார்கள், எனவே CSV தரவை மேப்களாக மாற்றும் ஒரு சார்பை எழுதுவோம்.

முதலில், csv-data→maps என்ற பெயரில் ஒரு சார்பை வரையறுப்போம்:

(defn csv-data->maps [])

இந்த சார்பு csv-data என்ற ஆர்கியுமெண்ட்டை எடுத்துக்கொள்ள வேண்டும்:

(defn csv-data->maps [csv-data])

CSV-ன் முதல் வரி தலைப்பாக (header) இருக்கும்:

(defn csv-data->maps [csv-data]
  (first csv-data))

முதல் வரியை கீவேர்டுகளாக (keywords) மாற்றுவோம்:

(defn csv-data->maps [csv-data]
  (->> (first csv-data) ;; முதல் வரி தலைப்பு
      (map keyword) ;; உங்களுக்கு ஸ்ட்ரிங் கீக்கள் வேண்டுமென்றால் இந்த வரியை நீக்கவும்
  ))

இப்போது ஒவ்வொரு தரவு வரிக்கும் தலைப்பை மீண்டும் பயன்படுத்துவோம்:

(defn csv-data->maps [csv-data]
  (->> (first csv-data) ;; முதல் வரி தலைப்பு
      (map keyword) ;; உங்களுக்கு ஸ்ட்ரிங் கீக்கள் வேண்டுமென்றால் இந்த வரியை நீக்கவும்
      repeat))

இப்போது (rest csv-data) மூலம் மீதமுள்ள தரவைப் பெறுவோம்:

(defn csv-data->maps [csv-data]
  (->> (first csv-data) ;; முதல் வரி தலைப்பு
      (map keyword) ;; உங்களுக்கு ஸ்ட்ரிங் கீக்கள் வேண்டுமென்றால் இந்த வரியை நீக்கவும்
      repeat)
  (rest csv-data))

இப்போது தலைப்பையும் மீதமுள்ள தரவையும் zipmap மூலம் இணைப்போம்:

(defn csv-data->maps [csv-data]
  (map zipmap
       (->> (first csv-data) ;; முதல் வரி தலைப்பு
            (map keyword) ;; உங்களுக்கு ஸ்ட்ரிங் கீக்கள் வேண்டுமென்றால் இந்த வரியை நீக்கவும்
            repeat)
       (rest csv-data)))

இத்துடன் முடிந்தது. இப்போது இதைச் சோதித்துப் பார்ப்போம்:

(csv-data->maps (csv-reader "in-file.csv"))

வெளியீடு (Output):

({:name "Karthikeyan", :profession "Clojure Programmer"} {:name "Pari", :profession "Data Engineer"})

இப்போது நமக்கு மேப்களின் வரிசை கிடைத்துள்ளது, இது Clojure புரோகிராமர்களுக்கு நன்கு தெரிந்த வடிவமாகும்.

முழு மூலக் குறியீடு இங்கே கிடைக்கிறது: https://gitlab.com/clojure-book/file-ops/-/raw/csv_ops/src/file_ops/csv_ops.clj

src/file_ops/csv_ops.clj
;; csv_ops.clj
;; https://github.com/clojure/data.csv

(ns file-ops.csv-ops
  (:require [clojure.data.csv :as csv]
            [clojure.java.io :as io]))

(defn csv-reader [filename]
  (with-open [reader (io/reader filename)]
    (doall
     (csv/read-csv reader))))

(csv-reader "in-file.csv")

(defn csv-writer [filename data]
  (with-open [writer (io/writer filename)]
    (doall
     (csv/write-csv writer data))))

(csv-writer "out-file.csv"
            [["name" "profession"]
             ["Karthikeyan" "Clojure Programmer"]
             ["Pari" "Data Engineer"]
             ["Bill Gates" "Shackles you up"]
             ["Richard M Stallman" "Unshackles you"]])

(defn csv-data->maps [csv-data]
  (map zipmap
       (->> (first csv-data) ;; முதல் வரி தலைப்பு
            (map keyword) ;; உங்களுக்கு ஸ்ட்ரிங் கீக்கள் வேண்டுமென்றால் இந்த வரியை நீக்கவும்
            repeat)
       (rest csv-data)))

(csv-data->maps (csv-reader "in-file.csv"))

30.4. JSON

குறிப்பு: இதன் பிராஜெக்ட் மூலக் குறியீட்டை இங்கே காணலாம் https://gitlab.com/clojure-book/file-ops/-/tree/json_ops/

குறிப்பு: இந்தப் பிரிவிற்கான வீடியோவை இங்கே பார்க்கலாம் https://yu7.in/clj-json

JSON என்பது JavaScript Object Notation என்பதன் சுருக்கமாகும். இது ஒரு எடை குறைந்த தரவு-பரிமாற்ற வடிவமாகும் (data-interchange format) மற்றும் ஜாவாஸ்கிரிப்ட்டின் ஒரு பகுதியாகும். ஒரு உரை வடிவமாக இருப்பதால், JSON எந்த மொழியையும் சாராதது மற்றும் ஒரு சர்வர் மற்றும் வலைப்பக்கத்திற்கு இடையே தரவை மாற்றப் பரவலாகப் பயன்படுத்தப்படுகிறது. இந்தப் பிரிவில், Clojure-இல் JSON-ஐ எவ்வாறு பயன்படுத்துவது என்று பார்ப்போம்.

கீழே உள்ள உள்ளடக்கத்துடன் numbers.json என்ற கோப்பை உருவாக்குவோம்:

numbers.json
{
    "one": 1,
    "two": 2,
    "three": 3
}

கீழே காட்டியபடி project.clj-இல் [org.clojure/data.json "2.5.1"] என்பதைச் சேர்க்கவும். இந்த வரி நமது பிராஜெக்டில் JSON-ஐப் பயன்படுத்தத் தேவையான லைப்ரரியை கொண்டு வரும்.

project.clj
(defproject file-ops "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0"
            :url "https://www.eclipse.org/legal/epl-2.0/"}
  :dependencies [[org.clojure/clojure "1.11.1"]
                 [org.clojure/data.csv "1.1.0"]
                 [org.clojure/data.json "2.5.1"]]
  :repl-options {:init-ns file-ops.core})

புதிய டிபென்டென்சி லோட் ஆவதற்கு REPL-ஐ ரீஸ்டார்ட் செய்யவும்.

src/file_ops/json_ops.clj என்ற கோப்பை உருவாக்கி, கீழே காட்டியபடி JSON லைப்ரரியை இணைக்கவும்:

(ns file-ops.json-ops
  (:require [clojure.data.json :as json]))

கீழே காட்டியபடி numbers.json கோப்பின் உள்ளடக்கத்தைப் படிப்போம்:

(def numbers
  (json/read-str (slurp "numbers.json")))

numbers

வெளியீடு (Output)

{"one" 1, "two" 2, "three" 3}

நீங்கள் காண்பது போல, (slurp "numbers.json") என்பது numbers.json-ன் உள்ளடக்கத்தைப் படிக்கிறது, அது பின்னர் json/read-str-க்கு அனுப்பப்பட்டு ஒரு மேப்பாக (map) மாற்றப்படுகிறது. இந்த முடிவை (def numbers (json/read-str (slurp "numbers.json"))) மூலம் numbers என்ற வேரியபிளில் சேமிக்கிறோம்.

இப்போது, numbers மேப்பில் ஒரு புதிய கீ-வேல்யூ (key-value) ஜோடியைச் சேர்ப்போம்:

(def more-numbers (assoc numbers "four" 4))

more-numbers

வெளியீடு (Output)

{"one" 1, "two" 2, "three" 3, "four" 4}

மேப்பில் "four" 4 என்ற ஜோடியைச் சேர்த்து, அதன் முடிவை more-numbers என்ற வேரியபிளில் சேமித்துள்ளோம்.

இப்போது, more-numbers மேப்பை more-numbers.json என்ற கோப்பில் எழுதுவோம்:

(spit "more-numbers.json" (json/write-str more-numbers))

more-numbers.json கோப்பைத் திறக்கும்போது, உள்ளடக்கம் கீழே காட்டியபடி இருக்கும்:

more-numbers.json
{"one":1,"two":2,"three":3,"four":4}

இந்த பிரிவிற்கான முழு மூலக் குறியீடு கீழே கொடுக்கப்பட்டுள்ளது, மேலும் நீங்கள் அதை இங்கிருந்து பெறலாம் https://gitlab.com/clojure-book/file-ops/-/raw/json_ops/src/file_ops/json_ops.clj

;; json_ops.clj
;; https://github.com/clojure/data.json

(ns file-ops.json-ops
  (:require [clojure.data.json :as json]))

(def numbers
  (json/read-str (slurp "numbers.json")))

numbers

(def more-numbers (assoc numbers "four" 4))

more-numbers

(spit "more-numbers.json" (json/write-str more-numbers))

30.5. YAML

குறிப்பு: இதன் பிராஜெக்ட் மூலக் குறியீட்டை இங்கே காணலாம் https://gitlab.com/clojure-book/file-ops/-/tree/yaml_ops

குறிப்பு: வீடியோவை இங்கே பார்க்கலாம் https://yu7.in/clj-yaml

YAML என்பது JSON-ன் எளிமையான வடிவமாகும் மற்றும் இது பெரும்பாலும் கான்ஃபிகுரேஷன் (configuration) கோப்புகளுக்குப் பயன்படுத்தப்படுகிறது. இந்தப் பிரிவில், Clojure-இல் YAML-ஐ எவ்வாறு பயன்படுத்துவது என்று பார்ப்போம். முதலில், [clj-commons/clj-yaml "1.0.29"] என்ற வரியைப் பயன்படுத்தி நமது பிராஜெக்டில் clj-yaml லைப்ரரியைச் சேர்ப்போம்.

project.clj
(defproject file-ops "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0"
            :url "https://www.eclipse.org/legal/epl-2.0/"}
  :dependencies [[org.clojure/clojure "1.11.1"]
                 [org.clojure/data.csv "1.1.0"]
                 [org.clojure/data.json "2.5.1"]
                 [clj-commons/clj-yaml "1.0.29"]]
  :repl-options {:init-ns file-ops.core})

இது நமது பிராஜெக்டில் YAML-ஐப் பயன்படுத்துவதற்குத் தேவையான லைப்ரரியை கொண்டு வரும்.

இப்போது, கீழே உள்ள உள்ளடக்கத்துடன் yaml_ops.clj என்ற கோப்பை உருவாக்குவோம்:

(ns file-ops.yaml-ops
  (:require [clj-yaml.core :as yaml]))

(:require [clj-yaml.core :as yaml]) என்ற வரி YAML லைப்ரரியை இணைக்கிறது, அதை yaml என்ற பெயரில் நாம் பயன்படுத்தலாம்.

YAML உள்ளடக்கத்தை ஒரு கோப்பிலிருந்து படிப்பதற்குப் பதிலாக, ஒரு வேரியபிளாகப் பயன்படுத்துவோம். பின்வரும் உள்ளடக்கத்துடன் some-yaml என்ற வேரியபிளை உருவாக்குவோம்:

(def some-yaml "
todo:
  issues:
    - name: Fix all the things
      responsible:
        name: Rita
")

இப்போது, (yaml/parse-string some-yaml) ஐப் பயன்படுத்தி some-yaml-ன் உள்ளடக்கத்தை ஒரு மேப்பாக மாற்றுவோம்:

(def some-data (yaml/parse-string some-yaml))

some-data

வெளியீடு (Output)

{:todo {:issues [{:name "Fix all the things", :responsible {:name "Rita"}}]}}

மேலே காண்பது போல, அனைத்து கீகளும் (keys) கீவேர்டுகளாக உள்ளன. உங்களுக்கு ஸ்ட்ரிங் கீக்கள் வேண்டுமென்றால், கீழே காட்டியபடி :keywords false என்பதைப் பயன்படுத்தவும்:

(yaml/parse-string some-yaml :keywords false)

வெளியீடு (Output)

{"todo" {"issues" ({"name" "Fix all the things", "responsible" {"name" "Rita"}})}}

இது கீகளை ஸ்ட்ரிங்குகளாக வழங்கும், மேலே காட்டியபடி.

இப்போது, (println (yaml/generate-string some-data)) ஐப் பயன்படுத்தி தரவை YAML வடிவத்தில் அச்சிடுவோம்:

(println (yaml/generate-string some-data :dumper-options {:indent 2
                                                 :flow-style :block}))

வெளியீடு (Output)

todo:
  issues:
  - name: Fix all the things
    responsible:
      name: Rita

வெளியீடு நேர்த்தாகவும் மனிதர்கள் படிக்கும் வகையிலும் இருக்க, நாம் இண்டென்டேஷனை (indentation) 2 எனவும், flow-style-ஐ block எனவும் அமைத்துள்ளோம். இந்த ஆப்ஷன்கள் இல்லையெனில், வெளியீடு இவ்வாறு இருக்கும்:

(println (yaml/generate-string some-data))

வெளியீடு (Output)

todo:
  issues:
  - name: Fix all the things
    responsible: {name: Rita}

முழு YAML செயல்பாட்டு வரிசை கீழே கொடுக்கப்பட்டுள்ளது. இந்தக் கோப்பை இங்கே காணலாம்: https://gitlab.com/clojure-book/file-ops/-/raw/yaml_ops/src/file_ops/yaml_ops.clj

;; yaml_ops.clj
;; https://github.com/clj-commons/clj-yaml/blob/master/doc/01-user-guide.adoc

(ns file-ops.yaml-ops
  (:require [clj-yaml.core :as yaml]))

(def some-yaml "
todo:
  issues:
    - name: Fix all the things
      responsible:
        name: Rita
")

(def some-data (yaml/parse-string some-yaml))

some-data

(yaml/parse-string some-yaml :keywords false)

(println (yaml/generate-string some-data :dumper-options {:indent 2
                                                 :flow-style :block}))

(println (yaml/generate-string some-data))

31. Clojure பிராஜெக்டில் ஜாவா கோப்புகள் (Java Files in Clojure Project)

குறிப்பு: இந்தப் பிரிவிற்கான வீடியோவை இங்கே காணலாம்: https://yu7.in/java-files-in-clojure-project

குறிப்பு: இதன் மூலக் குறியீட்டை (source) இங்கே காணலாம்: https://yu7.in/W9qT0X

Clojure ஜாவாவின் மேல் இயங்குவதால், ஜாவா புரோகிராம்களை Clojure-இல் பயன்படுத்துவது எளிது. இதை எப்படி செய்வது என்று பார்க்க, my-mixed-project என்ற பெயரில் ஒரு Leiningen பிராஜெக்டை உருவாக்குவோம்.

$ lein new app my-mixed-project

இப்போது, பிராஜெக்ட் டைரக்டரிக்குச் செல்லவும்:

$ cd my-mixed-project

my-mixed-project-ன் அமைப்பு கீழே காட்டியபடி இருப்பதை உறுதி செய்து கொள்ளுங்கள். இப்போது project.clj, core.clj, மற்றும் MyJavaClass.java ஆகியவற்றின் உள்ளடக்கங்களைப் பார்ப்போம்.

my-mixed-project/
├── project.clj
├── src/
│   ├── clojure/
│   │   └── my_mixed_project/core.clj
│   └── java/
│       └── my_mixed_project/MyJavaClass.java

முதலில், MyJavaClass.java கோப்பைப் பார்ப்போம்.

src/java/my_mixed_project/MyJavaClass.java
package my_mixed_project;

public class MyJavaClass {
    public static String greet(String name) {
        return "Hello from Java, " + name + "!";
    }
}

உங்களுக்கு ஜாவா தெரியவில்லை என்றால் கவலைப்பட வேண்டாம்—இந்தப் பகுதியை நீங்கள் தவிர்க்கலாம், அப்போதும் எல்லாம் சரியாக வேலை செய்யும். ஜாவா தெரிந்தவர்களுக்கு, இந்தக் கோப்பு my_mixed_project என்ற பேக்கேஜையும் (package), MyJavaClass என்ற கிளாஸையும் (class), மற்றும் name என்ற ஸ்ட்ரிங் அளவுருவை (String parameter) ஏற்கும் greet என்ற ஸ்டேடிக் மெத்தடை (static method) வரையறுக்கிறது.

இப்போது, கீழே உள்ள project.clj கோப்பைப் பாருங்கள். :java-source-paths ["src/java"] என்ற வரியைக் கவனியுங்கள்; நமது ஜாவா மூலக் கோப்புகள் src/java ஃபோல்டரில் உள்ளன என்று இது Leiningen-க்கு தெரிவிக்கிறது.

project.clj
(defproject my-mixed-project "0.1.0-SNAPSHOT"
  :description "A Leiningen project with both Clojure and Java"
  :dependencies [[org.clojure/clojure "1.11.1"]]
  :source-paths ["src/clojure"]
  :java-source-paths ["src/java"]
  :main my-mixed-project.core)

அதேபோல், :source-paths ["src/clojure"] என்ற வரி நமது Clojure மூலக் கோப்புகள் src/clojure ஃபோல்டரில் உள்ளன என்று Leiningen-க்கு தெரிவிக்கிறது.

இப்போது, src/clojure/my_mixed_project ஃபோல்டரில், core.clj என்ற கோப்பை உருவாக்கி பின்வரும் குறியீட்டைச் சேர்க்கவும்.

src/clojure/my_mixed_project/core.clj
(ns my-mixed-project.core
  (:import [my_mixed_project MyJavaClass]))

(defn -main [& args]
  (println (MyJavaClass/greet "Clojure Developer")))

core.clj-ல் உள்ள குறியீட்டை படிப்படியாகப் பார்ப்போம். முதலில், நைம்ஸ்பேஸை (namespace) வரையறுக்கிறோம்:

src/clojure/my_mixed_project/core.clj
(ns my-mixed-project.core)

அடுத்து, my_mixed_project ஜாவா பேக்கேஜிலிருந்து இறக்குமதி செய்ய விரும்புவதை குறிப்பிடுகிறோம்:

src/clojure/my_mixed_project/core.clj
(ns my-mixed-project.core
  (:import [my_mixed_project]))

பின்னர், குறிப்பிட்ட MyJavaClass-ஐ இறக்குமதி செய்கிறோம்:

src/clojure/my_mixed_project/core.clj
(ns my-mixed-project.core
  (:import [my_mixed_project MyJavaClass]))

இப்போது, மெயின் (main) சார்பை வரையறுப்போம்:

src/clojure/my_mixed_project/core.clj
(ns my-mixed-project.core
  (:import [my_mixed_project MyJavaClass]))

(defn -main [& args])

மெயின் சார்பிற்குள், நாம் MyJavaClass/greet மெத்தடை அழைக்கிறோம்:

src/clojure/my_mixed_project/core.clj
(ns my-mixed-project.core
  (:import [my_mixed_project MyJavaClass]))

(defn -main [& args]
  (MyJavaClass/greet))

இப்போது, அதற்கு "Clojure Developer" என்ற ஆர்கியுமெண்ட்டை வழங்குகிறோம்:

src/clojure/my_mixed_project/core.clj
(ns my-mixed-project.core
  (:import [my_mixed_project MyJavaClass]))

(defn -main [& args]
  (MyJavaClass/greet "Clojure Developer"))

இறுதியாக, வெளியீட்டை அச்சிட இந்த அழைப்பை println சார்பு மூலம் சுற்றி வைக்கிறோம்:

src/clojure/my_mixed_project/core.clj
(ns my-mixed-project.core
  (:import [my_mixed_project MyJavaClass]))

(defn -main [& args]
  (println (MyJavaClass/greet "Clojure Developer")))

இப்போது, பிராஜெக்டை இயக்குவோம்:

$ lein run
Hello from Java, Clojure Developer!

Hello from Java, Clojure Developer! என்ற வெளியீடு கன்சோலில் அச்சிடப்படும்.

நாம் வெற்றிகரமாக ஒரு ஜாவா கோப்பை எழுதி அதை ஒரு Clojure பிராஜெக்டில் ஒருங்கிணைத்துள்ளோம்.

32. Clojure பிராஜெக்டில் JAR கோப்புகள் (JAR files in Clojure project)

குறிப்பு: இந்தப் பிரிவிற்கான வீடியோவை இங்கே காணலாம் https://yu7.in/jar-files-in-clojure-project

இந்த பிரிவில், Clojure பிராஜெக்ட்களில் JAR கோப்புகளை எவ்வாறு இணைப்பது என்று பார்ப்போம்.

32.1. JAR கோப்பை உருவாக்குதல் (Creating a JAR file)

குறிப்பு: இந்தப் பிரிவிற்கான மூலக் குறியீட்டை (source code) இங்கே காணலாம் https://gitlab.com/clojure-book/create_jar_file

உங்கள் சொந்த JAR கோப்புகளை எவ்வாறு உருவாக்குவது என்று தெரிந்துகொள்ள விரும்பினால், இந்தப் பகுதியை வாசியுங்கள்; இல்லையெனில், அடுத்த பகுதிக்குச் செல்லுங்கள். create_jar என்ற பெயரில் ஒரு டைரக்டரியை உருவாக்குவோம்.

$ mkdir create_jar

டைரக்டரிக்குச் செல்லவும்:

$ cd create_jar

myjar என்ற பெயரில் ஒரு ஃபோல்டரையும், அதற்குள் HelloPrinter.java என்ற கோப்பையும் உருவாக்குங்கள். ஃபோல்டர் அமைப்பு கீழே காட்டியபடி இருக்க வேண்டும்:

.
└── myjar
    └── HelloPrinter.java

இப்போது HelloPrinter.java-வில் பின்வரும் குறியீட்டைச் சேர்க்கவும்:

// HelloPrinter.java

package myjar;

public class HelloPrinter {
    public static void printHello() {
        System.out.println("Hello, World from the jar!");
    }
}

மேலே உள்ள குறியீட்டில், myjar என்ற பேக்கேஜைப் பயன்படுத்துகிறோம், இது HelloPrinter.java அமைந்துள்ள ஃபோல்டரின் பெயருடன் பொருந்துகிறது. அடுத்து, HelloPrinter என்ற கிளாஸ் உள்ளது. இந்த கிளாஸில் "Hello, World from the jar!" என்ற செய்தியை அச்சிடும் printHello என்ற மெத்தட் உள்ளது.

இப்போது javac-ஐப் பயன்படுத்தி இந்தக் கோப்பை கம்பைல் (compile) செய்வோம்:

$ javac -d java-classes myjar/HelloPrinter.java

கம்பைல் செய்த பிறகு, java-classes/myjar/ என்ற ஃபோல்டர் உருவாக்கப்பட்டு, அதற்குள் HelloPrinter.class என்ற கிளாஸ் கோப்பு இருப்பதைக் காண்பீர்கள். இந்த .class கோப்பு ஜாவா பைட்-கோடை (Java bytecode) கொண்டுள்ளது.

.
├── java-classes
│   └── myjar
│       └── HelloPrinter.class
└── myjar
    └── HelloPrinter.java

இப்போது இதை ஒரு JAR கோப்பாக மாற்ற வேண்டும். அதற்கு, பின்வரும் கமாண்ட்டைப் பயன்படுத்தவும்:

$ jar cf hello-printer.jar -C java-classes .

கிடைக்கும் கோப்பு அமைப்பு இவ்வாறு இருக்கும்:

.
├── hello-printer.jar
├── java-classes
│   └── myjar
│       └── HelloPrinter.class
└── myjar
    └── HelloPrinter.java

hello-printer.jar கோப்பு உருவாக்கப்பட்டுள்ளது; இதுதான் நாம் பயன்படுத்தப்போகும் கோப்பு.

32.2. JAR கோப்பைப் பயன்படுத்துதல் (Using the JAR file)

குறிப்பு: இந்தப் பிரிவிற்கான மூலக் குறியீட்டை இங்கே காணலாம் https://gitlab.com/clojure-book/hello_jar_clj

JAR கோப்பைப் பயன்படுத்த, ஒரு Leiningen பிராஜெக்டை உருவாக்குவோம்:

$ lein new hello-jar-clj

பிராஜெக்ட் அமைப்பு இவ்வாறு இருக்க வேண்டும்:

hello-jar-clj/
├── CHANGELOG.md
├── doc
│   └── intro.md
├── lib
│   └── hello-printer.jar
├── LICENSE
├── project.clj
├── README.md
├── resources
├── src
│   └── hello_jar_clj
│       └── core.clj
├── target
└── test
    └── hello_jar_clj
        └── core_test.clj

இப்போது, நீங்கள் உருவாக்கிய JAR கோப்பை lib ஃபோல்டரில் நகலெடுக்கவும் (copy). பின்னர், project.clj-ல் உள்ள :classpath மதிப்பை கவனியுங்கள்:

project.clj
(defproject hello-jar-clj "0.1.0-SNAPSHOT"
  :description "Demo using Java jar in Clojure"
  :dependencies [[org.clojure/clojure "1.11.1"]]
  :classpath ["lib/hello-printer.jar"]
  :main hello-jar-clj.core)

JAR கோப்பு இருக்கும் சரியான இடத்தைக் குறிப்பிடுகிறோம். இப்போது, src/hello_jar_clj/core.clj-ல் பின்வரும் குறியீட்டைச் சேர்க்கவும்:

src/hello_jar_clj/core.clj
(ns hello-jar-clj.core
  (:import [myjar HelloPrinter]))

(defn -main [& args]
  (HelloPrinter/printHello))

குறியீட்டை விளக்குகிறேன். முதலில், hello-jar-clj.core என்ற நைம்ஸ்பேஸை வரையறுக்கிறோம்.

src/hello_jar_clj/core.clj
(ns hello-jar-clj.core)

அடுத்து, myjar பேக்கேஜிலிருந்து HelloPrinter கிளாஸை இறக்குமதி செய்கிறோம்.

src/hello_jar_clj/core.clj
(ns hello-jar-clj.core
  (:import [myjar HelloPrinter]))

பின்னர், மெயின் (main) சார்பை வரையறுக்கிறோம்:

src/hello_jar_clj/core.clj
(ns hello-jar-clj.core
  (:import [myjar HelloPrinter]))

(defn -main [& args])

மெயின் சார்பிற்குள், நாம் HelloPrinter/printHello மெத்தடை அழைக்கிறோம்.

src/hello_jar_clj/core.clj
(ns hello-jar-clj.core
  (:import [myjar HelloPrinter]))

(defn -main [& args]
  (HelloPrinter/printHello))

இப்போது, பிராஜெக்டை இயக்குவோம்:

$ lein run

நமக்குக் கிடைக்கும் வெளியீடு:

Hello, World from the jar!

33. உங்களுக்கென சொந்தமாக லைப்ரரிகளை உருவாக்குதல் (Creating Your Own Libraries)

குறிப்பு: இந்தப் பிரிவிற்கான வீடியோவை இங்கே பார்க்கலாம்: https://youtu.be/QWhpISM-d5Q

இந்த பிரிவில், ஒரு சிறிய Leiningen பிராஜெக்டை உருவாக்கி, அதை Clojars-இல் பதிவேற்றி, அந்த லைப்ரரியை மற்றொரு பிராஜெக்டில் இறக்குமதி செய்து பயன்படுத்துவது எப்படி என்று பார்ப்போம்.

33.1. ஒரு Clojure பிராஜெக்டை உருவாக்குதல் (Creating a Clojure Project)

குறிப்பு: இந்தப் பிரிவிற்கான மூலக் குறியீட்டை (code) இங்கே பெறலாம்: https://gitlab.com/clojure-book/printline

முதலில், printline என்ற சிறிய பிராஜெக்டை உருவாக்குவோம்:

$ lein new printline

இப்போது, printline டைரக்டரிக்குச் சென்று src/printline/core.clj கோப்பின் உள்ளடக்கத்தை இவ்வாறு மாற்றவும்:

(ns printline.core)

(defn print-line
  ([char length]
   (println (apply str (repeat length char))))
  ([length-or-char]
   (if (int? length-or-char)
     (print-line \- length-or-char)
     (print-line length-or-char 80))))

பிராஜெக்டில் ஜாக்-இன் (jack-in) செய்து (பார்க்கவும் [11]), REPL-இல் print-line சார்பைச் (function) சோதித்துப் பார்ப்போம்:

(print-line "*")
; ********************************************************************************
nil
(print-line "$" 20)
; $$$$$$$$$$$$$$$$$$$$
nil
(print-line 30)
; ------------------------------
nil
testing printline in repl

இது சரியாக வேலை செய்வது போல் தெரிகிறது. இப்போது, Clojure லைப்ரரிகளை ஹோஸ்ட் செய்யும் இடமான Clojars-இல் நம்மைப் பதிவு செய்து கொள்வோம்.

33.2. Clojars

https://clojars.org/ என்ற இணையதளத்திற்குச் செல்லுங்கள். உங்களுக்கு இது போன்ற ஒரு பக்கம் தோன்றும்:

clojars

மேல் வலதுபுறத்தில் உள்ள 'Register' என்ற லிங்க்கை கிளிக் செய்யவும்.

33.2.1. பதிவு செய்தல் (Registering)

இப்போது Clojars பதிவுப் பக்கம் தோன்றும்; உங்கள் விவரங்களை நிரப்பவும்.

registering

நான் ஏற்கனவே பதிவு செய்து லாக்-இன் செய்திருப்பதால், எனது டேஷ்போர்டு (dashboard) எப்படி இருக்கும் என்பதை கீழே உள்ள படம் காட்டுகிறது.

dashboard

டேஷ்போர்டிற்கு வந்தவுடன், மேலே உள்ள சாம்பல் நிற நேவிகேஷன் பாரில் 'Group Verification' என்ற மெனுவை கிளிக் செய்யவும்.

33.2.2. ஒரு குழுவை உருவாக்குதல் (Creating a group)

நீங்கள் Group Verification பக்கத்திற்கு அழைத்துச் செல்லப்படுவீர்கள். ஒரு குழுவை சரிபார்க்கும் (verify) எளிதான வழி 'Verification by Parent Group' என்பதைப் பயன்படுத்துவதாகும்.

GroupVerification

எனக்கு ஏற்கனவே org.clojars.mindaslab என்ற பேரண்ட் குரூப் ஒதுக்கப்பட்டுள்ளது (இங்கு mindaslab என்பது எனது பயனர் பெயர்). இப்போது, 'Group name' ஃபீல்டில், printline என்ற சப்-குரூப்பை உருவாக்க வேண்டும், எனவே நான் org.clojars.mindaslab.printline என்று நிரப்புகிறேன். பின்னர் 'Verify Group' பட்டனை கிளிக் செய்யவும்.

கீழே காட்டியபடி, எனது குழுவான org.clojars.mindaslab.printline சரிபார்க்கப்பட்டது என்ற மஞ்சள் நிற அறிவிப்பு தோன்றும்.

GroupVerified

இப்போது மேலே உள்ள சாம்பல் நிற நேவிகேஷன் பாரில், 'DEPLOY TOKENS' என்ற மெனு இருக்கும். அதை கிளிக் செய்யவும்.

33.2.3. டெப்ளாய்மென்ட் டோக்கனைப் பெறுதல் (Getting a deployment token)

Deploy Tokens பிரிவில், கீழே காட்டியபடி:

DeployTokens

டோக்கனுக்கு நீங்கள் நினைவில் வைத்துக்கொள்ளக்கூடிய ஒரு பெயரை வழங்கவும்; நான் "Printline Deploy Token" என்று பயன்படுத்தியுள்ளேன். Scope-க்கு org.clojars.mindaslab.printline-ஐத் தேர்ந்தெடுத்துள்ளேன். இந்த டோக்கன் காலாவதியாகக் கூடாது என்பதால், "Expires in" ஃபீல்டில் அதை குறிப்பிட்டுள்ளேன். பின்னர் 'Create Token' பட்டனை கிளிக் செய்யவும்.

உங்களுக்கு இது போன்ற ஒரு திரை தோன்றும்:

DeployTokensObtained

மஞ்சள் நிறப் பட்டியில் உள்ள Clojars…​.. என்ற டோக்கனை காப்பி செய்து கொள்ளுங்கள். பிராஜெக்டை டெப்ளாய் (deploy) செய்ய இந்த டோக்கனைப் பயன்படுத்துவோம்.

33.3. Clojars-இல் பதிவேற்றுதல் (Uploading to Clojars)

இப்போது, உங்கள் printline பிராஜெக்டில், project.clj கோப்பில் இதைச் சேர்க்கவும்:

:repositories [["clojars" {:url "https://clojars.org/groups/org.clojars.mindaslab.printline"
                             :sign-releases false}]]

உங்கள் லைப்ரரியை எந்த நைம்ஸ்பேஸிற்கு (namespace) அனுப்புகிறீர்கள் என்பதை இது Clojars-க்கு தெரிவிக்கிறது. உங்கள் project.clj இவ்வாறு இருக்க வேண்டும்:

(defproject printline "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0"
            :url "https://www.eclipse.org/legal/epl-2.0/"}
  :dependencies [[org.clojure/clojure "1.11.1"]]
  :repl-options {:init-ns printline.core}
  :repositories [["clojars" {:url "https://clojars.org/groups/org.clojars.mindaslab.printline"
                             :sign-releases false}]])

இப்போது, project.clj-ல் பிராஜெக்ட் பெயரை printline-லிருந்து org.clojars.mindaslab.printline என மாற்றவும்:

(defproject org.clojars.mindaslab.printline "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0"
            :url "https://www.eclipse.org/legal/epl-2.0/"}
  :dependencies [[org.clojure/clojure "1.11.1"]]
  :repl-options {:init-ns printline.core}
  :repositories [["clojars" {:url "https://clojars.org/groups/org.clojars.mindaslab.printline"
                             :sign-releases false}]])

இப்போது, பிராஜெக்ட் ரூட் டைரக்டரியில், இந்த கமாண்ட்டை இயக்கவும்:

$ lein deploy clojars

இது உங்களிடம் பயனர் பெயரை (username) கேட்கும், அதை வழங்கவும்.

$ lein deploy clojars
WARNING: please set :description in project.clj.
WARNING: please set :url in project.clj.
No credentials found for clojars
See `lein help deploying` for how to configure credentials to avoid prompts.
Username: mindaslab

பின்னர் அது கடவுச்சொல்லைக் (password) கேட்கும், Clojars-இல் இருந்து நீங்கள் பெற்ற டோக்கனை வழங்கவும்.

❯ lein deploy clojars
WARNING: please set :description in project.clj.
WARNING: please set :url in project.clj.
No credentials found for clojars
See `lein help deploying` for how to configure credentials to avoid prompts.
Username: mindaslab
Password:
Created /Users/mindaslab/author/clojure/projects/printline/target/org.clojars.mindaslab.printline-0.1.0-SNAPSHOT.jar
Wrote /Users/mindaslab/author/clojure/projects/printline/pom.xml
Could not find metadata org.clojars.mindaslab.printline:org.clojars.mindaslab.printline:0.1.0-SNAPSHOT/maven-metadata.xml in clojars (https://repo.clojars.org/)
Sending org/clojars/mindaslab/printline/org.clojars.mindaslab.printline/0.1.0-SNAPSHOT/org.clojars.mindaslab.printline-0.1.0-20251015.115436-1.jar (9k)
    to https://repo.clojars.org/
Sending org/clojars/mindaslab/printline/org.clojars.mindaslab.printline/0.1.0-SNAPSHOT/org.clojars.mindaslab.printline-0.1.0-20251015.115436-1.pom (2k)
    to https://repo.clojars.org/
Could not find metadata org.clojars.mindaslab.printline:org.clojars.mindaslab.printline/maven-metadata.xml in clojars (https://repo.clojars.org/)
Sending org/clojars/mindaslab/printline/org.clojars.mindaslab.printline/0.1.0-SNAPSHOT/maven-metadata.xml (1k)
    to https://repo.clojars.org/
Sending org/clojars/mindaslab/printline/org.clojars.mindaslab.printline/maven-metadata.xml (1k)
    to https://repo.clojars.org/

சில வினாடிகளில், லைப்ரரி டெப்ளாய் செய்யப்படும்.

இப்போது, https://clojars.org/org.clojars.mindaslab.printline என்ற பக்கத்திற்குச் சென்றால், அதை எவ்வாறு பயன்படுத்துவது என்று Clojars சொல்லும்.

printline after deployed

33.4. உங்கள் லைப்ரரியைப் பயன்படுத்துதல் (Using Your Library)

குறிப்பு: இந்தப் பிரிவிற்கான மூலக் குறியீட்டை இங்கே பெறலாம் https://gitlab.com/clojure-book/try_printline

இந்த பிரிவில், நமது லைப்ரரியை எவ்வாறு பயன்படுத்துவது என்று பார்ப்போம். try_printline என்ற புதிய பிராஜெக்டை உருவாக்குவோம்.

பக்கத்தில் காட்டியுள்ள டிபென்டென்சியை (dependency) காப்பி செய்து கொள்ளுங்கள்: [org.clojars.mindaslab.printline "0.1.0-SNAPSHOT"] (பக்கம்: https://clojars.org/org.clojars.mindaslab.printline)

printline after deployed

இப்போது try_printline என்ற புதிய Leiningen பிராஜெக்டை உருவாக்குவோம்.

$ lein new try_printline

project.clj-ல், :dependencies வெக்டரில் [org.clojars.mindaslab.printline "0.1.0-SNAPSHOT"] என்பதைச் சேர்க்கவும். அது இவ்வாறு இருக்க வேண்டும்:

(defproject try_printline "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0"
            :url "https://www.eclipse.org/legal/epl-2.0/"}
  :dependencies [[org.clojure/clojure "1.11.1"]
                 [org.clojars.mindaslab.printline "0.1.0-SNAPSHOT"]]
  :repl-options {:init-ns try-printline.core})

try_printline பிராஜெக்டில் ஜாக்-இன் செய்து, src/try_printline/core.clj கோப்பை இவ்வாறு மாற்றவும்:

(ns try-printline.core
  (:require [printline.core :as pl]))


(pl/print-line "*")

(pl/print-line 30)

(pl/print-line "$", 40)

இங்கே (:require [printline.core :as pl]) என்ற அறிக்கையின் மூலம் printline.core-ஐ நாம் இறக்குமதி செய்வது கவனியுங்கள்.

பின்வரும் குறியீட்டை இயக்கவும்:

(ns try-printline.core
  (:require [printline.core :as pl]))

இது nil என்று பதிலளிக்கும். இப்போது இதை இயக்கவும்:

(pl/print-line "*")

REPL-இல் பின்வரும் வெளியீட்டைக் காண்பீர்கள்:

; ********************************************************************************
nil

இதை இயக்கவும்:

(pl/print-line 30)

உங்களுக்கு இது கிடைக்கும்:

; ------------------------------
nil

இறுதியாக இதை இயக்கவும்:

(pl/print-line "$", 40)

உங்களுக்கு இது கிடைக்கும்:

; $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
nil

இவ்வாறாக நாம் வெற்றிகரமாக ஒரு லைப்ரரியை உருவாக்கி, அதை Clojars-இல் சேர்த்து, நமது பிராஜெக்டில் இறக்குமதி செய்து பயன்படுத்தினோம். Clojars-இல் பதிவேற்றுவதில் மகிழ்ச்சி!

34. மெட்டாடேட்டா (Metadata)

குறிப்பு: இதன் மூலக் குறியீட்டை (source) இங்கே பெறலாம் https://yu7.in/XkFT5d

குறிப்பு: வீடியோவை இங்கே பார்க்கலாம் https://yu7.in/5U0vRr

Clojure-இல், ஒரு சார்பை (function) பிரைவேட் (private) ஆக அறிவிக்க இரண்டு வழிகள் உள்ளன: ஒன்று defn- மூலம், கீழே காட்டியபடி:

(defn- private-function []
  "private function")

அல்லது மெட்டாடேட்டாவுடன் defn-ஐப் பயன்படுத்தலாம், கீழே காட்டியபடி:

(defn ^:private private-function []
  "private function")

மெட்டாடேட்டா என்பது ஒரு வரையறைக்கு (definition) நீங்கள் ஒதுக்கக்கூடிய தரவு ஆகும். இதன் மூலம் கம்பைலருக்கோ (compiler) அல்லது அந்த வரையறையைப் பயன்படுத்தும் புரோகிராமிற்கோ சில குறிப்புகளை வழங்க முடியும். ^:private மெட்டாடேட்டாவில் நீங்கள் பார்த்தபடி, இது ஒரு பூலியன் (boolean) மதிப்பாகும். இது true என அமைக்கப்பட்டால், அந்த சார்பு மற்ற நைம்ஸ்பேஸ்களில் (namespaces) இருந்து அணுகப்படுவதைத் தடுக்கிறது.

இந்த பிரிவில் மெட்டாடேட்டாவைப் பற்றி ஆழமாகப் பார்ப்போம்.

கீழே உள்ள குறியீட்டில், some-var-with-meta என்ற வேரியபிளை வரையறுக்கிறோம், மேலும் அதற்கு ^:boo என்ற மெட்டாடேட்டாவை ஒதுக்குகிறோம்.

(def ^:boo some-var-with-meta 7)
;; => #'user/some-var-with-meta

இப்போது மெட்டாடேட்டா இல்லாத some-var என்ற மற்றொரு வேரியபிளை வரையறுப்போம்.

(def some-var 7)
;; => #'user/some-var

இந்த இரண்டு வேரியபிள்களையும் ஒப்பிடுவோம்.

(= some-var some-var-with-meta)
;; => true

some-var-with-meta-விற்கு மெட்டாடேட்டா ஒதுக்கப்பட்டிருந்தாலும், அது some-var-க்கு சமமாகவே உள்ளது. Clojure-இல் மெட்டாடேட்டா ஒரு வேரியபிளின் இயல்பான செயல்பாட்டைப் பாதிப்பதில்லை.

இப்போது some-var-with-meta-வின் மெட்டாடேட்டாவை எடுத்துப் பார்ப்போம்:

(meta #'some-var-with-meta)
;; => {:boo true, :line 1, :column 1, :file "/Users/mindaslab/author/clojure/code/meta_data.clj", :name some-var-with-meta, :ns #object[clojure.lang.Namespace 0xf7f1972 "user"]}

மேலே காண்பது போல, நமக்கு மெட்டாடேட்டா ஒரு மேப்பாக (map) கிடைக்கிறது, அங்கு :boo என்ற சிம்பல் (symbol) true என உள்ளது. #'some-var-with-meta-வில் உள்ள #' என்பது என்ன என்று நீங்கள் யோசிக்கலாம். இது ஒரு ரீடர் மேக்ரோ (reader macro) ஆகும். இது வேரியபிளின் மதிப்பான 7-ஐக் குறிக்காமல், some-var-with-meta என்ற வேரியபிளையே குறிக்கிறது.

ஒரு வரையறைக்கு ஒரே ஒரு மெட்டாடேட்டா மட்டுமே ஒதுக்க முடியும் என்பதில்லை; பல கீகளும் (keys) மதிப்புகளும் கொண்ட மேப்பினை நீங்கள் ஒதுக்கலாம்:

(def ^{:a 1 :b 2 :c 3} lots-of-meta 42)
;; => #'user/lots-of-meta

இப்போது lots-of-meta-வின் மெட்டாடேட்டாவைக் கேட்டால், நமக்குக் கிடைப்பது:

(meta #'lots-of-meta)
;; => {:a 1, :b 2, :c 3, :line 13, :column 1, :file "/Users/mindaslab/author/clojure/code/meta_data.clj", :name lots-of-meta, :ns #object[clojure.lang.Namespace 0xf7f1972 "user"]}

:a, :b, மற்றும் :c தவிர மேப்பில் மற்ற கீகளும் இருப்பதை கவனியுங்கள், ஆனால் அவற்றைப் பற்றி நாம் விரிவாகப் பார்க்கப்போவதில்லை.

முந்தைய உதாரணங்களில், meta ஐ அழைத்தபோது வேரியபிளைப் பற்றிய நிறைய தகவல்கள் கிடைத்தன, ஆனால் with-meta மூலம் ஒரு வேரியபிளை வரையறுக்கும்போது தேவையற்ற தகவல்கள் தவிர்க்கப்படுகின்றன:

(def my-list (with-meta '(1 2 3) {:secret "meta"}))
;; => #'user/my-list

மேலே உள்ள உதாரணத்தில் நாம் ஒரு வேரியபிளை வரையறுக்கிறோம்:

(def my-list)

பின்னர் with-meta சார்பைப் பயன்படுத்துகிறோம்:

(def my-list (with-meta ....))

with-meta-விற்கு முதல் ஆர்கியுமெண்ட்டாக வரையறை மதிப்பைச் சேர்க்கவும்:

(def my-list (with-meta '(1 2 3) ....))

with-meta-விற்கு இரண்டாவது ஆர்கியுமெண்ட்டாக மெட்டாடேட்டாவைச் சேர்க்கவும்:

(def my-list (with-meta '(1 2 3) {:secret "meta"}))

அவ்வளவுதான்.

இப்போது my-list-ன் மதிப்பைச் சரிபார்ப்போம்:

my-list
;; => (1 2 3)

இப்போது my-list-ன் மெட்டாடேட்டாவைச் சரிபார்ப்போம்:

(meta my-list)
;; => {:secret "meta"}

மெட்டாடேட்டாவைக் கேட்கும்போது இது மிகவும் நேர்த்தியாக உள்ளது.

கவனித்தீர்களா? நாம் meta-வை my-list-ன் மீது பயன்படுத்துகிறோம், #'my-list மீது அல்ல. அதற்குக் காரணம், with-meta சார்பு மெட்டாடேட்டாவை my-list-ன் மதிப்பிற்கு ('(1 2 3)) இணைக்கிறது, my-list என்ற பெயரையும் அதன் மதிப்பையும் கொண்ட என்டிட்டிக்கு (entity) அல்ல.

^:private ஒரு சார்புடன் இணைக்கப்பட்டு, அதை பிரைவேட் ஆக மாற்றி, வரையறுக்கப்பட்ட நைம்ஸ்பேஸிற்குள் மட்டுமே அணுகக்கூடியதாக மாற்றுவதை நாம் ஏற்கனவே பார்த்தோம். அதேபோல், எந்த ஒரு கீவேர்டையும் (keyword) ஒரு Clojure சார்பிற்கு மெட்டாடேட்டாவாக இணைக்கலாம். கீழே நாம் ஒரு சார்புடன் {:something true} என்பதை இணைக்கிறோம்:

(defn ^:something my-function []
  "some function")
;; => #'user/my-function

நாம் my-function-ஐ அழைக்கிறோம்:

(my-function)
;; => "some function"

இது எதிர்பார்த்தபடியே வேலை செய்கிறது. ஆனால் கீழே காண்பது போல, அதன் மெட்டாடேட்டாவைக் கேட்கும்போது, :something என்ற கீவேர்டு true என்ற மதிப்பைக் கொண்டுள்ளது.

(meta #'my-function)
;; => {:something true, :arglists ([]), :line 29, :column 1, :file "/Users/mindaslab/author/clojure/code/meta_data.clj", :name my-function, :ns #object[clojure.lang.Namespace 0xf7f1972 "user"]}

டாக்ஸ்ட்ரிங்குகளும் (Docstrings) மெட்டாடேட்டாவே ஆகும், கீழே உள்ள சார்பு ஒரு டாக்ஸ்ட்ரிங்கைக் கொண்டுள்ளது.

(defn function-with-doc
  "This is a function with doc string"

  []
  (println "some function"))
;; => #'user/function-with-doc

இப்போது, (meta #'function-with-doc) மூலம் மெட்டாடேட்டாவைக் கேட்டு, கீழே காட்டியபடி :doc என்ற கீவேர்டுடன் தொடர்புடைய மதிப்பை எடுத்தால்:

(:doc (meta #'function-with-doc))
;; => "This is a function with doc string"

உங்களுக்கு டாக்ஸ்ட்ரிங் வெளியீடாகக் கிடைக்கும்.

ஒரே விளைவை, ஒரு சார்பிற்கு :doc என்ற கீவேர்டை மெட்டாடேட்டாவாக அமைப்பதன் மூலமும் பெறலாம், கீழே காட்டியபடி:

(defn ^{:doc "This is another function with doc string"} another-function-with-doc

  []
  (println "some function"))
;; => #'user/another-function-with-doc

(:doc (meta #'another-function-with-doc))
;; => "This is another function with doc string"

கீழே காட்டியபடி, பல கீவேர்டுகள் மற்றும் மதிப்புகளைக் கொண்ட ஒரு மேப்பை ஒரு சார்பிற்கு மெட்டாடேட்டாவாக ஒதுக்கலாம்.

(defn ^{:a 1 :b 2 :c 3} lots-of-meta-my-function []
  "some function")
;; => #'user/los-of-meta-my-function

அதன் meta-வை நாம் கேட்கும்போது, Clojure மறைமுகமாக அமைக்கும் மற்ற மெட்டாடேட்டாக்களுடன் சேர்த்து, அந்த கீவேர்டுகளும் மதிப்புகளும் நமக்குக் கிடைக்கும், கீழே காட்டியபடி:

(meta #'lots-of-meta-my-function)
;; => {:ns #object[clojure.lang.Namespace 0xf7f1972 "user"], :name los-of-meta-my-function, :file "/Users/mindaslab/author/clojure/code/meta_data.clj", :column 1, :c 3, :line 52, :b 2, :arglists ([]), :a 1}

Bibliography


1. https://en.wikipedia.org/wiki/Localhost
2. https://en.wikipedia.org/wiki/Port_(computer_networking)
3. https://en.wikipedia.org/wiki/Random-access_memory
4. https://en.wikipedia.org/wiki/Immutable_object
5. https://clojuredocs.org/clojure.spec.alpha/cat
6. https://clojuredocs.org/clojure.spec.alpha/keys
7. https://clojuredocs.org/clojure.core/seq
8. I got this list from http://rubular.com:
9. https://clojuredocs.org/clojure.test
10. https://yu7.in/wc68VQ
11. https://calva.io/jack-in-guide/