Jump to content

Recommended Posts

a6dde3b9073f.png

 

Clojure არის ახალი პროგრამირების ენა, რომელიც შესასრულებლად იყენებს ჯავა ვირტუალურ მანქანას. Clojure არის უფრო ძველი პროგრამირების ენის, ლისპის, დიალექტი.

ეს დოკუმენტი წარმოადგენს მოკლე შესავალს Clojure პროგრამირების ენაში, განსაკუთრებით სასარგებლო ის იქნება მათვის, ვინც აქამდე C-ს მსგავს პროგრამირების ენას იყენებდა.

C-ს მსგავს ენებში, ფუნქციის გამოძახება, როგორც წესი, ასე გამოიყურება:

do_something_with(value1, value2)

Clojure-ში იგივე ფუნქციის გამოძახება ასე გამოიყურება:

(do-something-with value1 value2)

 

 

აი რაშია Clojure განსხვავებული:

  • ფრჩხილი იხსნება ფუნქციის სახელის მარცხნივ.
  • არა გვაქვს პარამეტრებს შორის არანაირი გამყოფი (მაგალითად, მძიმე).
  • მიღებულია, რომ სიტყვები ფუნქციის სახელში გაიყოს მოკლე ტირეებით.

არც ისე ბევრი განსხვავებაა.

აი როგორ გამოიყურება ორი რიცხვის შეკრება C-ს მსგავს ენაში:

value1 + value2

აქ კი მოყვანილია, თუ როგორ გამოიყურება ეს Clojure-ში:

(+ value1 value2)

ამ შემთხვევაში კიდევ ერთი განსხვავებაა, პლიუსის ნიშანი მოდის პარამეტრების წინ. C-ს მსგავს ენაში პლიუსის ნიშანო ოპერატორია. Clojure-ში კი ის მხოლოდ ფუნქციაა, სახელად ‘+’. Clojure არ ჰყოფს "ოპერატორის" და "ფუნქციის" ცნებებს, როგორც ამას აკეთებენ C-ს მსგავსი ენები. ყველაფერი, რაც არის ოპერატორი C-ს მსგავს ენაში, Clojure-ში უნდა წარმოდგენილი იყოს ფუნქციის სახით. თუ თქვენი საყვარელ C-ს მსგავს ენას გამოვაცლით ყველა ოპერატორს, მაშინ ორი მნიშვნელობის დამატება ასე შეიძლება რომ გამოიყურებოდეს:

add(value1, value2)

რადგანაც ახლა თქვენი C-ს მსგავსი ენა არ შეიცავს ოპერატორებს, ის სიმბოლოები, რომლებიც ადრე ოპერატორისთვის გჭირდებოდათ, შეიძლება გამოიყენოთ ფუნქციის სახელად. მაგალითად, ასე:

+(value1, value2)

ახლა ეს დიდად არ განსხვავდება Clojure-ს შესაბამისი გამოსახულებისგან:

(+ value1 value2)

 

 

Clojure/Clojure-ს დაყენება

 

Clojure-ს დასაყენების ორი გზა არსებობს: ოფიციალური რელიზიდან ან სუბვერსიის (Subversion) რეპოზიტორიუმიდან.

აქ იგულისხმება, რომ თქვენ უკვე დაყენებული გაქვს ჯავას ვირტუალური მანქანა, და იცით როგორ გამოიყენოთ თქვენი სისტემის შესაბამისი ტერმინალის პროგრამა.

 

დაყენება ოფიციალური რელიზიდან

 

  1. შედით http://clojure.org/-ს საწყის გვერდზე, გაჰყევით Download ბმულს, და გადმოიწერეთ Clojure-ს პაკეტი. სავარაუდოდ ეს არქივირებული (ZIP) ფაილია.
  2. განაარქივეთ ZIP ფაილი, თუ თქვენი ბრაუზერი ამას ავტომატურად არ აკეთებს.
  3. შემდეგ თქვენი ტერმინალით შედით ('cd' ბრძანების გამოყენებით) Clojure-ს განარქივებულ საქაღალდეში.
  4. შეიყვანეთ ტერმინალში ბრძანება: java -cp clojure.jar clojure.main.
  5. თქვენ უნდა დაინახოთ ბრძანების შეტყობინება: user=>

 

 

დაყენება სუბვერსიის რეპოზიტორიუმიდან

თუ თქვენ გაქვთ დაყენებული სუბვერსია (Subversion) და Ant, რეკომენდებულია, ნაცვლად რელიზის გადმოწერისა, თქვენით ამოიღოთ Clojure საწყისი კოდი რეპოზიტორიუმიდან. თქვენს სისტემაზე უკვე უნდა ეყენოს ჯავა 5, Ant და სუბვერსია. ასე შეგიძლიათ დაყენება ტერმინალის გამოყენებით:

 

  1. შეიყვანეთ ტერმინალში შემდეგი ბრძანებები:
  2. svn co https://clojure.svn.sourceforge.net/svnroot/clojure/trunk clojure 
  3. cd clojure
  4. ant
  5. java -cp clojure.jar clojure.main
  6. თქვენ უნდა დაინახოთ ბრძანების შეტყობინება: user=>

  7. ბრძანების შეტყობინება ცნიბილია სახელით REPL (Read-Evaluate-Print-Loop). REPL კითხულობს (Read) გამოსახულებებს, რომლებიც თქვენ შეგყავთ, აფასებს (Evaluate) მათ, ბეჭდავს (Print) შედეგს და ბრუნდება უკან (Loop) ახალი ბრძანების მისაღებად.

 

Clojure-ში არის ორი ტიპის გამოსახულება: ატომები და სიები. ატომები გავს პრიმიტიულ ტიპებს სხვა პროგრამირების ენებში. გამოვიყენოთ REPL ზოგიერთი ატომის განსახილველად.

რიცხვები

user=> 5
5

რიცხვი 5 ფასდება და შედეგი გამოდის ეკრანზე.

ბულის სიმბოლოები

აი სხვა ატომი:

user=> true
true

Clojure-ში მხარდაჭერილია ბულის "ჭეშმარიტი" და "მცდარი" მნიშვნელობები, რომლებიც შესაბამისად ჩაიწერება როგორც true და false.

Nil

აი კიდევ ერთი მნიშვნელოვანი ატომი:

user=> nil
nil

ეს არის Clojure-ს სახელი ცარიელი მნიშვნელობისთვის. ის შეესაბამება ჯავას null-ს.

ტექსტი

აი Clojure-ს ტექსტი:

user=> "Hello, world!" 
"Hello, world!"

Clojure ტექსტი იგივე წესებით განისაზღვრება, როგორც ჯავას ტექსტი, ასე მაგალითად "\t" წარმოადგენს ტაბულაციის სიმბოლოს. ჯავას API არის ძირითადი საშუალება ტექსტზე მოქმედებისა.

სიმბოლოები

სიმბოლოები არის სახელები რომლებიც ჩვეულებრივად ებმება მნიშვნელობას. თუმცა მნიშვნელობაზე მიბმა არაა აუცილებელი . სიმბოლოს აქვს შესაბამისი ობიექტი (არა მიბმული მნიშვნელობა), და ამ ობიექტის მისაღებად, სიმბოლოს სახელს წინ დაურთეთ ერთმაგი ბრჭყალი:

user=> 'x
x

თუ თავად მნიშვნელობის მიღება გინდათ, გამოიყენეთ უშუალოდ სიმბოლოს სახელი:

user=> x
1

თუ სიმბოლო არაა მნიშვნელობასთან მიბმული, თქვენ შეგიძლიათ ამ სიმბოლოს თავისთავად გამოყენება, მაგრამ შეცდომაა ამ სიმბოლოს მნიშვნელობის მიღების მცდელობა:

user=> foo
java.lang.Exception: Unable to resolve symbol: foo in this context
...

კვანძი სიტყვები

კვანძი სიტყვები გვანან სიმბოლოებს, თუმცა ისინი არ ებმებიან მნიშვნელობებს და შეფასებისას საკუთარ თავს აბრუნებენ მნიშვნელობად. კვანძი სიტყვები ყოველთვის იწყებიან ორწერტილით (:). აი რამოდენიმე კვანძი სიტყვა:

user=> :a
:a
user=> :_123
:_123
user=> :KEY
:KEY

კვანძი სიტყვები განსაკუთრებით მოსახერხებელია გამოვიყენოთ რუკებში, მნიშვნელობის გასაღებად.

 

Clojure/სიები

 

ატომები შეიძლება დაჯგუფდეს სიებში. სიები შეიძლება იყოს ცარიელი ან შედგებოდეს რამდენიმე ატომისაგან, რომლებიც მოქცეულია ფრჩხილებში. სიის ელემენტები გამიჯნულია ცარიელი სიმბოლოთი (შესაძლებელია აგრეთვე მძიმით გამიჯვნაც). სიები შეიძლება გამოვიყენოთ მოქმედების შესასრულებლად, მაგალითად ასეთის:

user=> (+ 3 3 3)
9

ამ ოპერაციით ხდება სამი რიცხვის შეჯამება. როდესაც Clojure აფასებს სიებს, სიის პირველი ელემენტი, რომელსაც ოპერატორს უწოდებენ, განსაზღვრავს შესასრულებელი ოპერაციის ტიპს. დანარჩენ ელემენტებს ეწოდებათ არგუმენტები.

სია, რომელიც ასრულებს მოქმედებას ცნობილი ოპერატორით არის ფორმა. სულ სამი სახის ფორმა არსებობს: ფუნქციები, მაკროსები და სპეციალური ფორმები.

სიის ელემენტები შეიძლება იყოს ატომები, სიები ან სხვა მონაცემთა სტრუქტურები რომლებიც შედიან Clojure-ში.

user=> (list :foo (list 1 2 3) [4 5 6])
(:foo (1 2 3) [4 5 6])

თუმცა სია მოქმედების შესასრულებლად გამოიყენება, იგივე სინტაქსის გამოყენებით შეიძლება აგრეთვე მონაცემის წარმოდგენაც. არსებითი შედეგი იმისა, რომ კოდი და მონაცემი ერთი სინტაქსით განისაზღვრება არის შემდეგი დებულება: კოდზე შეგვიძლია იმდაგვარად ვიმოქმედოთ, როგორც მონაცემზე, რადგან კოდი -- მონაცემია.

მონაცემთა სიის შესაქმენლად, გამოიყენეთ Clojure-ს ჩადგმული ოპერატორი, list:

user=> (list 1 2 3)
(1 2 3)

user=> (list a b c)
(a b c)

user=> (list "one" "two" "three")
("one" "two" "three")

Clojure-ს აგრეთვე აქვს უფრო მოკლე სინტაქსი მონაცემთა სიითვის. ამისათვის საკმარისია სიის წინ დაისვას ერთმაგი ბრჭყალი:

user=> '(1 2 3)
(1 2 3)

თუმცა ამ ხერხით სიის შექმნას აქვს მცირედით განსხვავებული ეფექტი. სიის ყოველი ელემენტები შეუფასებელი რჩება.

Clojure-ს ჩადგმული ოპერატორების დახმარებით შესაძლებელია მონაცემიდან მივიღოთ ინფორმაცია. შემდეგი გამოსახულება აბრუნებს მოცემული სიის პირველ ელემენტს.

user=> (first '("one" "two" "three"))
"one"

შემდეგი ოპერაცია კი აბრუნებს ყველა ელემენტს გარდა პირველისა:

user=> (rest '("one" "two" "three"))
("two" "three")

 

Clojure/ვექტორები, რუკები და სიმრავლეები

 

სიების მონაცემთა ტიპის გარდა, Clojure გვაძლევს ვექტორების, რუკების და სიმრავლეების განსაზღვრის სინტაქს. ვექტორები არის ნულოვან-ინდექსიანი მასივები. აი მაგალითები:

user=> [1 2 3]
[1 2 3]

user=> []
[]

user=> ["a" "b" "c"]
["a" "b" "c"]

user=> [:foo "bar" 3]
[:foo "bar" 3]

user=> [1 2 [10 20]]
[1 2 [10 20]]

ვექტორები ძალიან გვანან სიებს. განსხვავება არის საბაზისო მონაცემთა სტრუქტურაში. სიებს და ვექტორებს სხვადასხვა წარამადობა ახასიათებთ.

კიდევ ერთი განსხვავებაა, რომ ვექტორის სინტაქსი არ იწვევს ოპერაციის შესრულებას. მხოლოდ სიის სინტაქსით შეიძლება ოპერაციის შესრულება.

თქვენ აგრეთვე შეგიძლიათ ვექტორის შექმნა ელემენტების გადაცემით vector ოპერატორზე:

user=> (vector 1 2 3)
[1 2 3]

რუკები განსაზღვრავენ უნიკალურ გასაღები-მნიშვნელობა წყვილების სიმრავლეს.

user=> {"a" 1, "b" 2, "c" 3}
{"a" 1, "b" 2, "c" 3}

ეს რუკა აწყვილებს ტექსტს "a" რიცხვთან 1"b" რიცხვთან 2, და "c" რიცხვთან 3. მძიმეები წყვილებს შორის არააუცილებელია, და საჭიროა კოდის კითხვადობის გასაზრდელად. Clojure მათ თითქმის ისევე განიხილავს როგორც ცარიელი ადგილის სიმბოლოს. მძიმის დასმა შეგიძლიათ გამოსახულების ელემენტებს შორის ნებისმიერ ადგილას:

user=> {"a" 1 "b" 2 "c" 3}
{"a" 1, "b" 2, "c" 3}

user=> {"a", 1, "b", 2, "c", 3}
{"a" 1, "b" 2, "c" 3}

user=> {"a"  1 ,"b" 2 ,"c" 3}  
{"a" 1, "b" 2, "c" 3}

დააკვრდით, რომ Clojure-მ იცის თუ სად იყო გამოყენებული მძიმეები, თუმცა ეს უფრო მეტი თავსებადობისთვის კეთდება მხოლოდ.

როდესაც რუკა განსაზღვრულია, მისი მნიშვნელობების ამოღება შეგიძლიათ get ფორმის გამოყენებით:

user=> (get {"a" 1, "b" 2, "c" 3} "a")
1

user=> (get {"a" 1, "b" 2, "c" 3} "b")
2

user=> (get {"a" 1, "b" 2, "c" 3} "c")
3

user=> (get {"a" 1, "b" 2, "c" 3} "d")
nil

თუმცა ამ ყველაფრის უფრო მარტივად მიღების გზაც არსებობს:

user=> ({"a" 1, "b" 2, "c" 3} "a")
1

user=> ({"a" 1, "b" 2, "c" 3} "b")
2

user=> ({"a" 1, "b" 2, "c" 3} "c")
3

user=> ({"a" 1, "b" 2, "c" 3} "d")
nil

რუკები შეიძლება გამოვიყენოთ მათი გასაღების ფუნქციებად. ეს თავიდან ცოტა უცნაურია, მაგრამ ნელ-ნელა ეს აღნიშვნები სულ უფრო გასაგები გახდება.

აი მესამე გზა, თუ როგორ მიიღოთ მნიშვნელობა გასაღებიდან:

user=> (:a {:a 1, :b 2, :c 3})
1

user=> (:b {:a 1, :b 2, :c 3})
2

user=> (:c {:a 1, :b 2, :c 3})
3

user=> (:d {:a 1, :b 2, :c 3})
nil

მნიშვნელოვანია, რომ ბოლო ორი გამოყენების სცენარი კარგად იცოდეთ, იმიტომ რომ ისინი ხშირად შეგხვდებათ Clojure-ს პროგრამებში.

Clojure/ცვლადების და ფუნქციების გამოცხადება

 

def

გლობალური ცვლადის განსაზღვრისთვის გამოიყენება def ფორმა:

user=> (def x 5)
#'user/x

user=> x
5

user=> (+ 5 x)
10

user=> (def my-list '(1 2 3))
#'user/my-list

user=> my-list
(1 2 3)

user=> (last my-list)
3

როდესაც ცვლადი იქმნება ხდება შემდეგი რამ. ის რასაც def აბრუნებს არის var, რომელიც არის ობიეტქი, რომელსაც აქვს მნიშვნელობა, მაგალითად 5. აგრეთვე იქმნება სიმბოლო, რომელიც ებმება ამ var-ს.

defn

ფუნქციები შეიძლება შეიქმნას defn-ის გამოყენებით:

user=> (defn election-year? [year]
  (zero? (rem year 4)))

#'user/election-year?
user=> (election-year? 2007)
false

user=> (election-year? 2008)
true

ფუნქცია არის ობიექტის ნაირსახეობა, რომლის გამოძახება შეიძლება.

პირვალი არგუმენტი defn-ში არის ფუნქციის სახელი, რომელიც იქნება სიმბოლო, მიბმული ამ ფუნქციაზე. მეორე არგუმენტი არის ფუნქციის არგუმენტების სია. არგუმენტების სია ყოველთვის ვექტორით გამოისახება. defn-ის დარჩენილი არგუმენტები შეიძლება იყოს ერთი ან რამდენიმე გამოსახულება. ბოლო გამოსახულების შედეგი არის ფუნქციის დასაბრუნებელი მნიშვნელობა.

fn-ის გამოყენება

ანონიმური ფუნქცია შეიძლება შეიქმნას fn-ის გამოყენებით:

user=> (fn [x] (+ x 1))
user.eval__2384$fn__2386@c4b579

user=> ((fn [x] (+ x 1)) 9)
10

რადგანაც ფუნქციები უბრალოდ ობიექტებია, ისინი შეიძლება სიმბოლოს მიებას (მიენიჭოს ცვლადს):

user=> (def plus-one
     (fn [x] (+ x 1)))
#'user/plus-one

user=> (plus-one 9)
10

ფორმა defn სინამდვილეში არის მაკროსი, რომელსაც თავისი შიგთავსი გადაჰყვანს def + fn კომბინაციაში.

doc ფორმა

თითქმის ყველა ფორმას Clojure-ში აქვს ჩადგმული დოკუმნეტაცია. თუ გინდათ სწრაფად გაიგოთ მეტი ფორმის შესახებ ინფორმაცია, გადაეცით ფორმის სახელი doc ფორმში:

user=> (doc first)
-------------------------
clojure/first
([coll])
  Returns the first item in the collection. Calls seq on its
    argument. If coll is nil, returns nil.
nil

ფუნქციის დოკუმენტირება

არსებობს ფუნქციის დოკუმენტირების რამოდენიმე გზე. აი უმარტივესი:

user=> (defn plus-one
  "Returns a number one greater than x" 
  [x]
  (+ x 1))
#'user/plus-one

user=> (doc plus-one)
-------------------------
user/plus-one
([x])
  Returns a number one greater than x
nil

აი მეორე საშუალებაც:

user=> (defn plus-one
  {:doc "Returns a number one greater than x"}
  [x]
  (+ x 1))
#'user/plus-one
user=> (doc plus-one)
-------------------------
user/plus-one
([x])
  Returns a number one greater than x
nil

Clojure/სპეციალური ფორმები

 

Clojure-ს აქვს რამდენიმე ჩადგმული ფორმა, რომლებსაც ერთობლიობაში სპეციალურ ფორმებს უწოდებენ. ამ თავში ჩვენ ვიხილავთ ძირითად სპეციალურ ფორმებ, აგრეთვე განვიხილავთ Clojure-ში შესაძლებელი დამატებითი გამოსახულების ტიპებს.

 

str ფორმა

str ფორმა ორი ან მეტი მნიშვნელობის კონკატენაციისთვის გამოიყენება, რომელსაც ეს მნიშვნელობები საჭიროების შემთხვევაში ტექსტში გადაყავს (აკონვერტირებს):

user=> (str "Hello," " world!")
"Hello, world!" 

user=> (str 5)
"5" 

user=> (str "Value: " 5)
"Value: 5"

if ფორმა

if ფორმა იდენტურია if გამოსახულება C-ს მსგავს ენაში.

user=> (if true "yes")
"yes" 

user=> (if false "yes")
nil

user=> (if false "yes" "no")
"no" 

user=> (if nil "yes" "no")
"no" 

user=> (if "" true)
true

user=> (if 0 true)
true

user=> (if true "yes" "no")
"yes" 

user=> (if (= 1 1) "yes" "no")
"yes" 

user=> (if (= 1 1) (+ 2 3) (+ 5 5))
5

user=> (if (= 1 2) (+ 2 3) (+ 5 5))
10

თუ პირველი არგუმენტი, შეფასების და კონვერტირების შემდეგ, არის true, მაშინ გამოსახულება აბრუნებს მეორე არგუმენტს, წინააღმდეგ შემთხვევაში, მესამე არგუმენტი ბრუნდება. ბოლო, მესამე, არგუმენტი არააუცილებელია.

Clojure-ში, როდესაც მნიშვნელობა კონვერტირდება, ის ყოველთვის არის true, თუ ის არ არის false ან nil.

if ფორმა ძალიან წააგავს ? ოპერატორის მოქმედებას C-ს მსგავს ენებში:

v = true ? 1 : 0

do ფორმა

do ფორმა გამოიყენება რამოდენიმე ოპერაციის თანმიმდევრობით შესასრულებლად. ფუნქციონალურ პროგრამირებაში ჩვეულებრივად გამოსახულებები შედიან სხვა გამოსახულების შემადგენლობაში ან შეიცავენ სხვა გამოსახულებებს, ასე რომ არ არის საჭიროება ერთი მეორეს მიყოლებით ოპერაციების შესრულების. კარგია, როდესაც გამოსახულება აბრუნებს მნიშვნელობას, რომელსაც შემდეგ გამოიყენებს შემცველი გამოსახულება. თუმცა არის შემთხვევები, როდესაც გამოსახულების დაბრუნებული მნიშვნელობა არ გამოიყენება. თუ ასეთი გამოსახულება მაინც რამე სასარგებლოს აკეთებს, ამბობენ, რომ მას აქვსგვერდითი ეფექტი. მაგალითისთვის, ჩაწერა სტანდარტულ გამოსასვლელზე ან ფაილში ან მონაცემთა ბაზაში, ყველა არის გვერდითი ეფექტების მაგალითი.

Clojure-ში გვაქვს println ფორმა, სტანდარტულ გამოსავალზე ჩასაწერად. თუ გვინდა რომ println გამოვიყენოთ ისეთი გამოსახულების შიგნით, რომლის მნიშვნელობის გამოყენებას შემდეგ ვაპირებთ, ჩვენ ის უნდა ჩავდგათ do გამოსახულებაში:

user=> (do (println "Hello.") (+ 2 2))
Hello.
4

user=> (do (println "Hello.") (println "Hello again.") (+ 2 2))
Hello.
Hello again.
4

do ოპერაცია ასრულებს ყოველ გამოსახულებას თანმიმდევრობით და აბრუნებს ბოლო გამოსახულების მნიშვნელობას.

do არ არის ერთადერთი, რომელიც გაძლევთ საშუალებას რამოდენიმე ოპერაცია თანმიმდევრობით შეასრულოთ. letdefn და fn ყველა იძლევა საშუალებას გააკეთოთ იგივე.

ერთი რამ, რასაც უნდა მიეჩვიოთ, არის ის, რომ Clojure ფუნქციონალური ენაა. ყველა გამოსახულება Clojure-ში აბრუნებს მნიშვნელობას. ხშირად, ერთადერთი Clojure გამოსახულებამ შესაძლოა დაიკავოს რამოდენიმე ხაზი, სადაც C-ს მსგავსი პროგრამისტი დაწერდა იგივე ლოგიკას როგორც კოდის ბლოკს, შემდგარს რამოდენიმე გამოყოფილი გამოსახულებიდან. გამოყოფილი გამოსახულებები შესაძლოა ანიჭებდეს მნიშვნელობას ცვლადს, რომლიც შემდგომ გამოსახულებაში გამოიყენება. ფუნქციონალურ ენებში დაწერილი პროგრამებს აქვთ უფრო დიდი გამოსახულებები გაშლილი რამოდენიმე ხაზად, და არა რამოდენიმე ბლოკი დაყოფილი მცირე გამოსახულებებად.ასეთმა სტიმლმა შესაძლოა წაგართვათ დრო, სანამ მიეჩვევით, თუმცა რომ ისწავლით, ახალი სტილი ისეთივე ადვილია, როგორც ძველი. არის რამოდენიმე უპირატესობა ასე წეროთ თქვენი პროგრამები.

when ფორმა

when ფორმა გავს if ფორმას. განსხვავება ისაა რომ არა გვაქვს "else" პორობა, და შესაძლებელია რამოდენიმე გამოსახულების შესრულება თუ პირობა "ჭეშმარიტებაა".

user=> (when nil "Should return 'nil'")
nil

user=> (when false "Should return 'nil'")
nil

user=> (when true "Yes")
"Yes" 

user=> (when true 1)
1

user=> (when true 1 2 3)
3

user=> (when true (println "Hello, world") "Yes")
Hello, world
"Yes" 

user=> (when (= 5 (inc 4)) "Yes")
"Yes"

let ფორმა

let ოპერატორი გამოიყენება მნიშვნელობის განსაზღვრისათვის და ამ მნიშვნელობის გამოსაყენებლად შემადგენელ ოპერაციაში.

user=> (let [x 2] (+ x 8))
10

user=> (let [x 2 y 8] (+ x y))
10

user=> (let [x 2 y 8] (= (+ x y) 10))
true

user=> (let [x 3] (+ (* x x) 1))
10

user=> (let [color "Red"] (str "Color is: " color))
"Color is: Red" 

user=> (let [color "Red" phrase (str "Color is: " color)] (str "Clojure says: " phrase))
"Clojure says: Color is: Red"

let ფორმა ქმნის დროებით var-ს (x და y ამ შემთხვევაში), რომლის გამოყენება შეიძლება მხოლოდ let გამოსახულების შიგნით. ვექტორი გამოიყენება ამ var-ის და მისი მნიშვნელობის განსაზღვრისთვის, ვექტორი აგრეთვე გამოიყენება სხვა Clojure ფორმების მიერ დროებითი ცვლადების და მათი მნიშვნელობების განსაზღვისათვის. ვექტორი შეიცავს სახელი-მნიშვნელობის წყვილებს.

 

 

 

Clojure/ინტეგრაცია ჯავასთან

 

Clojure-ს აქვს საშუელაბე გამოიყენოს ჯავას ობიექტები და პრიმიტივები. ამის ცოდნა აუცილებელია რთული პროგრამების დასაწერად.

მოდით დავიწყოთ ჯავას java.util.Date ობიექტის შექმნით:

user=> (new java.util.Date)
Mon May 26 10:25:25 PDT 2008

Clojure ქმნის java.util.Date ობიექტს, შემდეგ იძახებს მის toString() მეთოდს, ამ ობიექტის ვიზუალურად წარმოდგენისთვის.

ობიექტის კონსტრუქტორში არგუმენტების გადასაცემად, უბრალოდ ჩართეთ ისინი new გამოძახებაში:

user=> (new StringBuffer "This is the initial value")
This is the initial value

ობიექტის მეთოდის გამოძახებისთვის გამოიყენეთ წერტილის (.) ფორმა:

user=> (. (new java.util.Date) (toString))
"Mon May 26 11:12:15 PDT 2008"

წერტილის ფორმა იყენებს წერტილის სიმბოლოს (.), როგორც ოპერატორს. მეორე არგუმენტია ობიექტი, რომლის მეთოდი უნდა გამოვიძახოთ. მესამე არგუემნტი არის სია, რომელიც შეიცავს მეთოდის სახელს და, თუ საჭიროა, მეთოდის არგუმენტებს:

user=> (. (new java.util.HashMap) (containsKey "key"))  
false

სტატიკური მეთოდები შესაძლებელია იგივე გზით გამოვიძახოთ:

user=> (. Boolean (valueOf "true"))
true

კლასის და ეგზემპლარის ველებიც იგივენაირად გამოიძახება:

user=> (. Integer MAX_VALUE)
2147483647

user=> (. Character TYPE)
char
</code>

(წარმატებას გისურვებთ, სტანდარტულ ჯავა ბიბლიოთეკაში ეგზემპლარის <code>public</code> ველის პოვნაში :)

განსხვავება აქ ისაა, რომ ველის სახელი არ არის მოქცეული ფრჩხილებში.

ისევე როგორც ჯავა პროგრამაში, Clojure გაძლევთ საშუალებას კლასების იმპორტირების მოცემულ კონტექსტში, ასე რომ კლასის გამოძახებისას არ იქნება საჭირო მათი სრული სახელის დაწერა:

<source lang="bash">
user=> (import '(java.io FileReader))
nil

user=> (new FileReader "source.txt")
java.io.FileReader@f784d7

იგივე კოდის შემოკლებული ვერსიაც არსებობს:

user=> (import '(java.io FileReader))
nil

user=> (FileReader. "source.txt")
java.io.FileReader@f784d7

ერთი პაკეტიდან რამოდენიმე კლასის იმპორტირება ასე ხდება:

user=> (import '(java.io File FileReader))
nil

ზედა მაგალითში მოხდა როგორც File, ასევე FileReader კლასების იმპორტირება.

თუ კლასები განსხვავებულ პაკეტებშია, გამოიყენეთ ასეთი სინტაქსი:

user=> (import '(java.io File) '(java.util HashMap))
nil

ან შეგიძლიათ ორი განცალკევებული import გამოსახულების გამოყენება:

user=> (import '(java.io File))
nil

user=> (import '(java.util HashMap))
nil

Clojure/ციკლები და იტერაციები

 

ქვემოთ ნაჩვენებია სამი ხერხი, თუ როგორ უნდა გავიაროთ ციკლი ინდექსით 0-დან 4-მდე (5 იტერაცია):

user=> (loop [i 0]
  (when (< i 5)
    (println i)
    (recur (inc i))))
0
1
2
3
4
nil
user=> (dorun (for [i (range 0 5)]
         (println i)))
0
1
2
3
4
nil
user=> (doseq i (range 0 5)
  (println i))
0
1
2
3
4
nil

პრიველი მაგალითი იყენებს loop ფორმას, რომელიც უზრუნველყოფს მაქსიმალურ მოქნილობას, თუმცა მოითხოვს ბევრ წერას. მეორე და მესამე მაგალითები არის თანმიმდევრობაზე იტერირების მაგალითები, რომელიც ციკლკის უფრო ხშირი მაგალითია. dorun და doseq გამოძახება ზღუდავს შემადგენელი ოპეტატორების მიერ მნიშვნელობის დაბრუნების შესაძლებლობას.

მოდით შევხედოთ loop ფორმას უფრო ახლოდან:

user=> (loop [i 0]
  (when (< i 5)
    (println "i:" i)
    (recur (inc i))))
i: 0
i: 1
i: 2
i: 3
i: 4
nil

ამ მაგალითში დროებითი სიმბოლო i მიბმულია მნიშვნელობაზე 0when გამოსახულება უყურებს, ეს მნიშვნელობა არის თუ არა 5-ზე ნაკლები. თუ ტესტი წარმატებულია, შიდა ორი გამოსახულება სრულდება. println გამოსახულება ბეჭდავს i-ს მნიშვნელობას. შემდეგ, recur სრულდება, რომელიც ავალებს ციკლს შესრულდეს კიდევ ერთხელ, i-ს ახალი მნიშვნელობით. (inc i) არის (+ i 1)-ს შემოკლება.

recur-ის გარეშე, loop გამოსახულება ზუსტად ისევე იქცევა, როგორც შესაბამისი let გამოსახულება.

 

 

 

Clojure/თანმიმდევრობები

 

თანმიმდევრობები (sequences) არსებითად არიას იდიომური Clojure პროგრამირების ბირთვი. თანმიმდევრობების და მათზე მომუშავე ფორმების გაგებით, თქვენ გადალახავთ ყველაზე რთულ ნაწილს, რომელიც საჭიროა სერიოზული Clojure პროგრამის წერაში.

ერთი შეხედვით თანმიმდევრობები ისევე გამოიყურება, როგორც სხვა მონაცემთა სტრუქტურები. თუმცა, თანმიმდევრობა არ არის მონაცემთა სტრუქტურა. ის ინტერფეისია, ან ხედი, მონაცემთა სტრუქტურაში. თანმიმდევრობა შეიძლება გამოყვანილ იყოს კოლექციიდან. დამოკიდებულება კოლექციასა და თანმიმდევრობას შორის წააგავს დამოკიდებულებას მონამცემთა ბაზის ცხრილს და მონაცემთა ბაზის ხედს (view) შორის.

Clojure-ს ოფიციალურ ვებ გვერდზე არის თანმიმდერობის შესანიშნავი განმარტება.

მოდით, მივიღოთ თანმიმდევრობა ვექტორიდან:

user=> (seq [1 2 3])
(1 2 3)

ამ კოდს ვექტორი უბრალოდ სიაში კი არ გადაჰყავს. ის ქმნის ვექტორის თანმიმდევრობას. REPL (Read, Evaluate, Print, Loop), როდესაც ცდილობს ეკრანზე გამოიყვანას, თანმიმდევრობას სიის სახით გვიჩვენებს.

ერთ-ერთი საშუალება REPL-ს აუკრძალოთ თანმიმდევრობის სიის სახით წარმოადგინოს, არის მოვაქციოთ ის სხვა გამოსახულებაში, რომელიც არ ცვლის თანმიმდევრობას. მაგალითად, გამოვიძახოთ მეთოდი ამ თანმიმდევრობაზე. ავიღოთ, მაგალითისთვის, getClass() მეთოდი:

user=> (.getClass (seq [1 2 3]))
clojure.lang.APersistentVector$Seq

ის რაც დაბრუნდა არის APersistentVector$Seq, რაც არის ვექტორის მიმდევრობის კლასი.

Clojure-ს ყველა ჩადგმულ სტრუქტურად აქვს მეთოდი თანმიმდევრობის მისაღებად. თანმიმდევრობის ინტერფეისი ფორმალურად არის clojure.lang.iSeq, ან უბრალოდ iSeq.

first

გამოიყენეთ first, რომ მიიღოთ თანმიმდევრობის პირველი ელემენტი:

user=> (first (seq [1 2 3]))
1

first-ს აგრეთვე შეუძლია არგუმენტად მიიღოს უშუალოდ ვექტორი, და არაცხადად გარდაქმნას ის თანმიმდევრობად:

user=> (first [1 2 3])
1

user=> (first ["a" "b" "c"])
"a" 

user=> (first '("A" "B" "C"))
"A" 

user=> (first '(:a :b :c))
:a

თანმიმდევრობის უმეტესი ფორმა ამ არაცხად კონვერტაციას აკეთებს, ასე რომ შეგიძლიათ გადასცეთ მათ ნებისმიერი კოლექცია რომელიც უზრუნველყოფსiSeq ინტერფეისს, რაც მოიცას Clojure-ს ყველა ჩადგმულ კოლექციის ტიპს.

rest

rest გვაძლევს თანმიმდევრობას, რომელიც შეიცაცს საწყისი თანმიმდევრობის ყველა წევრს, გარდა პირველისა.

user=> (rest [1 2 3])
(2 3)

user=> (rest ["a" "b" "c"])
("b" "c")

user=> (rest '("A" "B" "C"))
("B" "C")

user=> (rest [:a :b :c])
(:b :c)

გაითვალისწინეთ, რომ არანაირი ახალი მონაცემთა სტრუქტურა არ იქმნება. rest მხოლოდ ქმნის ლოგიკურ სიას (თანმიმდევრობას). მომხმარებელზეა დამოკიდებული შექმნის თუ არა ის აქედან მონაცემთა სტრუქტურას. ზედა მაგალითებში, გამომძახებელია REPL, რომელიც ცდილობს თანმიმდევრობის სიაში გადაყვანას, რომ გამოიტანოს რამე გასაგები ეკრანზე. გამოთვლის დროს, კომპიუტერის რესურსების ხარჯვის მხრივ, თანმიმდევრობის შექმნა მეტად ეკონომიურია.

cons

cons ქმნის ახალ თანმიმდევრობას პირველი არგუმენტის დამატებით მეორე არგუემნტის კოლექციაში.

user=> (cons 1 [2 3])
(1 2 3)

user=> (cons :a [:b :c])
(:a :b :c)

აქაც იგივეა, არანაირი ახალი მონაცემთა სტრუქტურა არ იქმნება cons-ის მიერ. შედგებილი თანმიმდევრობა შინაგანად შედგება მხოლოდ პირველი და მეორე არგუმენტებზე რეფერენსებისგან (pointer). თუმცა მომხმარებლისთვის ისე ჩანს თითქოს ეს ერთიანი თანმიმდევრობაა.

 

Edited by GHOST_FATHER
  • Upvote 1

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

×