Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
мова програмування Clojure. Процедурна парадигм...docx
Скачиваний:
0
Добавлен:
01.07.2025
Размер:
185.47 Кб
Скачать

Шаблони

Шаблони потрібні, коли:

потрібні прості текстові підстановки

коли підстановки потрібні лише в певному діапазоні імен

коли немає можливості використати функцію вищого порядку для виконання даної задачі

Простори імен та бібліотеки:

Простору імен ( namespaces ) використовуються в Clojure для організації коду і даних. За своїм характером , простору імен аналогічні пакетам ( packages ) в Common Lisp , і найчастіше вони використовуються при створенні бібліотек коду. Простори назв є об'єктами першого класу ( first class objects ) , і можуть динамічно змінюватися - створюватися , віддалятися , змінюватися , їх можна перераховувати і т. д. Користувач може управляти видимістю символів використовуючи метадані , або спеціальні макроси , такі як defn - , який визначає функцію , видиму тільки в поточному просторі імен.

Найбільш часто використовуваними функціями при роботі з просторами імен є:

use - Поміщає в поточне простір імен символи (все, або тільки зазначені ) з іншого простору імен, у тому числі і що знаходяться в інших бібліотеках , завантажуючи їх при необхідності ;

require - Завантажує задані бібліотеки , але не поміщає символи , визначені в них , в поточне простір імен ;

import - Використовується для бібліотек JVM і імпортує задані класи із зазначеного пакета.Кожна з цих функцій має різну кількість параметрів , опис яких можна знайти в документації. В якості приклад давайте розглянемо наступний код:

(use 'clojure.contrib.str-utils)

(require 'clojure.contrib.lazy-xml)

(require '[clojure.contrib.str-utils2 :as str2])

(import 'org.apache.commons.io.FileUtils)

(import '(java.io File InputStream))

Перший рядок завантажує бібліотеку clojure.contrib.str - utils і поміщає всі визначені в ній символи в поточне простір імен. Другий рядок завантажує бібліотеку clojure.contrib.lazy - xml , але для доступу до її об'єктів , необхідно використовувати повне ім'я символу , який включає назву простору імен. Третій рядок також завантажує бібліотеку , але створює псевдонім для назви простору імен , що дозволяє використовувати більш коротке ім'я символу , наприклад , str2/butlast . Четвертий приклад імпортує один клас ( FileUtils ) з пакету org.apache.commons.io , а в п'ятому рядку ми бачимо як можна імпортувати кілька класів з одного пакету .[6]

Робота зі змінними даними

Наявні засоби для роботи із змінними даними можна класифікувати за кількома параметрами , як показано у таблиці:

 Синхронне

Асинхронне

Координоване

ref

Незалежне

atom

agent

Ізольоване

var

Табл. 1. Параметри класифікаціх змінних даних

У Clojure є три механізму синхронного оновлення даних і один - асинхронного . Найбільш часто в коді використовуються посилання ( ref ) , які надають можливість синхронного поновлення даних в рамках транзакцій , і агенти ( agent ) , які реалізують механізми асинхронного оновлення даних. Крім цього , існують ще атоми , розглянуті нижче , і « змінні » ( var ) . «Змінні » мають « глобальне » ( root ) значення , яке визначено для всіх потоків виконання , але це значення можна перевизначати для окремих потоків виконання , використовуючи binding або set ! .

Агенти

Агенти ( agents ) дозволяють здійснювати асинхронне оновлення даних. Робота з агентами схожа на роботу з посиланнями (тільки ви повинні використовувати agent замість ref ) , але оновлення даних може статися в будь-який момент (і програміст не може на це впливати ) залежно від кількості завдань . Ці завдання виконуються в окремому пулі потоків виконання , розмір якого обмежений. На відміну від посилань , вам немає необхідності явно створювати транзакцію за допомогою функції dosync - ви просто посилаєте « повідомлення » , що складається з функції , яка встановить нове значення агента , і аргументів для цієї функції. Приклад з лічильниками, переписаний на використання агентів, буде виглядати наступним чином:

(def acounters (agent {}))

(defn add-acounter [key val]

(send acounters assoc key val))

(defn increment-acounter [key]

(send acounters assoc key (inc (@counters key 0))))

(defn get-acounter [key]

(@acounters key 0))

(defn rm-acounter [key]

(let [value (@acounters key)]

(send acounters dissoc key)

value))

Функції send і send-off отримують в якості аргументів ім'я агента, функцію, яку треба виконати, і додаткові параметри, які будуть передані викликається функції. Викликається функція отримує як аргумент поточний стан агента, і повинна повернути нове значення, яке отримає агент13. Під час свого виконання функція «бачить» актуальне значення агента.

Атоми

Атоми ( atoms ) надають можливість синхронного зміни незалежних даних , для яких не потрібна синхронізація в рамках транзакції. Робота з атомами схожа на роботу з посиланнями , тільки проводиться без координації : ви розкажете змінну за допомогою функції atom , можете отримати доступ до значення використовуючи deref (або @ ) і встановити нове значення за допомогою функції swap ! (або низкоуровневой функції compare - and - set !) .Зміни здійснюються за допомогою функції swap ! , Яка в якості аргументів приймає функцію і аргументи для цієї функції (якщо необхідно) . Передана функція застосовується до поточного значення атома для отримання нового значення , і потім робиться спроба атомарного зміни за допомогою compare - and - set ! . У тому випадку , якщо інший потік виконання вже змінив значення атома , то виклик користувача функції повторюється для обчислення нового значення , і знову робиться спроба зміни значення атома і т. д. , поки спроба зміни не буде успішною. Наприклад, код, який використовують атоми:

(def atom-counter (atom 0))

(defn increase-counter []

(swap! atom-counter inc)).[3]

Процедурна парадигма програмування

Структурний підхід до алгоритмізації

Розробка алгоритмів розв'язування задач на ЕОМ ставить деякі природні вимоги до алгоритмів: алгоритм має бути зрозумілим і легко сприйматися не тільки його розробником, необхідно, щоб алгоритм можна було легко перевірити, а також модифікувати його без суттєвої перебудови всієї структури.

Тому при розробці алгоритмів дотримуються особливої методики, що називається структурним підходом. За цим підходом алгоритми, як в конструкторі, “збираються” з трьох базових структур: слідування, розгалуження, цикл, кожна з яких має один вхід і один вихід.

Базова структура “слідування”

Ця структура складається із двох функціона­льних блоків Р1, Р2 і означає, що два функціональних бло­ка можуть розміщуватися один за одним.

Базова структура “розгалуження”

Ця структура складається з логічного блоку, у якому перевіряється деяка умова II, та функціональних блоків Р1, Р2. При цьому функціональний блок Р2 може бути відсутнім. Якщо умова виконується, то відбуваєть­ся вихід з блоку за стрілкою якщо ні - за стрілкою

Базова структура “цикл”

До складу структури входить логічний блок з перевіркою умови І і функціональний блок Р, який називається тілом циклу. Зі структури “цикл” слідує, що тіло Р може виконуватися неодноразово.

У залежності від взаємного розташування логічного та функціо­нального блоків, розрізняють цикли двох видів:

цикл-поки. тіло циклу розташоване після перевірки умови (мал. 3). Поки умова виконується, виконується і тіло. Тому можливий випадок, коли тіло циклу жодного разу не виконається. Тут У являється умовою продовження циклу (кожен раз, коли Іі істинно, тіло Р виконується);

цикл-до: Тіло циклу розташоване до перевірки умови (мал. 4), тому тіло циклу завжди виконається хоча б один раз, незалежно від виконання умови. Тут являється умовою виходу з циклу (як тільки II стає істинним, виконання тіла Р припиняється).[8]