суббота, 9 марта 2013 г.

Новое в clojure 1.5

1 марта был выпущен релиз clojure 1.5. Полный список изменений здесь https://github.com/clojure/clojure/blob/master/changes.md

Теперь для версии clojure 1.5 требуется Java 7 (используется класс java.util.concurrent.ForkJoinPool). Новая версия Java необходима для работы с новым добавленным неймспейсом clojure.core.reducers

Новая библиотека core.reducers призвана увеличить скорость обработки коллекций. Основная оптимизация заключается в том, что последовательность действий над коллекцией (например (filter #() (map #() seq))) не создает промежуточную коллекцию и действия могут выполняться параллельно(при этом код остаётся без изменений)

clojure-1-5.core=> (require ['clojure.core.reducers :as 'r])
nil
clojure-1-5.core=> (def numbers (into [] (range 99999)))
#'clojure-1-5.core/numbers
clojure-1-5.core=> (time (doseq [x (range 100)] (reduce + (r/filter #(> 4 (mod %1 10)) (r/map inc numbers)))))
"Elapsed time: 729.914422 msecs"
nil
clojure-1-5.core=> (time (doseq [x (range 100)] (reduce + (filter #(> 4 (mod %1 10)) (map inc numbers)))))
"Elapsed time: 847.422309 msecs"
nil

Для агентов появилась возможность задать собственный пул тредов для выполнения модифицирующих операций над агентами(с помощью send, send-of, send-via)
Например можно выполнять операции над агентами в один поток(по умолчанию число потоков равно числу ядер + 2)
clojure-1-5.core=> (def a (agent {:a 1}))
#'clojure-1-5.core/a
clojure-1-5.core=> @a
{:a 1}
clojure-1-5.core=> (send a assoc :b 2)
#<Agent@17d577c: {:a 1, :b 2}>
clojure-1-5.core=> @a
{:a 1, :b 2}
clojure-1-5.core=> (set-agent-send-executor! (java.util.concurrent.Executors/newSingleThreadExecutor))
#<FinalizableDelegatedExecutorService java.util.concurrent.Executors$FinalizableDelegatedExecutorService@108fbd4>
clojure-1-5.core=> (send a assoc :c 3)
#<Agent@17d577c: {:a 1, :c 3, :b 2}>
clojure-1-5.core=> @a
{:a 1, :c 3, :b 2}

Для кастомизации функции send используется set-agent-send-executor! для send-off используется  set-agent-send-off-executor!. Если нужно выполнить действие над агентом с произвольным исполнителем, можно использовать send-via.  В общем случае функцию send следует использовать когда операция занимает фиксированное количество времени, а send-off когда операция может быть заблокирована по I/O или другому внешнему ресурсу.

Начиная с версии 1.5 функции-конструкторы (hash-map, array-map) принимают дублирующие значения ключей и не выкидывают исключения

в версии 1.4
(hash-map :a 1 :b 2 :a 4)
IllegalArgumentException Duplicate key: :a  clojure.lang.PersistentHashMap.createWithCheck (PersistentHashMap.java:92)
в 1.5
(hash-map :a 1 :b 2 :a 4)
{:a 4, :b 2}
объявление литерала c дублирующими ключами по прежнему кидает исключение

{:a 1 :b 2 :a 4}
#<IllegalArgumentException java.lang.IllegalArgumentException: Duplicate key: :a>

К макросам -> ->> добавилось несколько новых макросов cond-> и cond->> для выполнения последовательности действий с условиями, а также есть возможность задать именнованную область с помощью as->

(-> 84
           (/ 4)
           (as-> twenty-one
                  (* 2 twenty-one)))
42
Для последовательности действий, где на промежуточном шаге результат действия может быть nil и необходимо прервать выполнение дальнейших шагов есть макрос some-> (или some->>)

(-> (range 1) next inc)
NullPointerException   [trace missing]
(some-> (range 1) next inc)
nil


Новая версия gen-class позволяет обращаться к protected final методам родительского класса через :exposes-methods

К мета данным (функция meta) добавлен атрибут :column, указывающий номер колонки в исходном файле. До версии 1.5 был указан только номер строки :line

четверг, 11 октября 2012 г.

Google использует Common Lisp?

Иначе зачем выпускать style-guide https://google-styleguide.googlecode.com/svn/trunk/lispguide.xml?

Нашел только одно упоминание про использование лиспа внутри google
http://googleresearch.blogspot.com/2008/11/plop-probabilistic-learning-of-programs.html

это plop
"A Common Lisp framework for experimenting with meta-optimizing semantic evolutionary search (MOSES) and related approaches to learning with probability distributions over program"

пятница, 20 января 2012 г.

Прошляпил Clojure Conj

Оказывается по Clojure была конференция Clojure Conj, аж на целых 3 дня. Проходила она в ноябре 2011 года. Видео доступно здесь.

Что самое приятное, так это то что есть люди заинтересованные в запуске и использовании clojure на Android. На clojure-conj был доклад на эту тему. Правда видео я не нашел. Если у кого есть напишите в комментариях.
Здесь ссылки на презентации докладчиков.

воскресенье, 16 октября 2011 г.

Опережающее объявление макроса

Кто-нибудь знает как в clojure сделать опережающее определение макроса?

Такой код не скомпилируется,  ошибка - "Unable to resolve symbol: my-inc in this context"

(my-inc 78)
(defmacro my-inc[number] `(+ ~number 1))


А так получишь ошибку в рантайме "Wrong number of args (1) passed to: "

(declare my-inc)
(my-inc 78)
(defmacro my-inc[number] `(+ ~number 1))


Я так понимаю declare приходится писать из за того что компилятор не многопроходный?

пятница, 1 июля 2011 г.

Создание экземпляра класса по имени

Для создания экземпляра класс в clojure можно воспользоваться специальной формой new

(new String)
или более краткой записью '.'

(String.)
Но не всегда этих средств достаточно. Возникает необходимость создания объекта класса, зная только имя целевого класса(например хранящегося в виде строки "String"). В этом случае объект можно создать воспользовавшись методами java-классов java.lang.Class(для классов с конструктором без параметров) и java.lang.reflect.Constructor - для конструкторов с параметрами.
Первый шаг создания объекта по имени класса - это найти полное имя класса, решается с помощью функции ns-resolve. Поиск выполняется в текущем пространстве имен(*ns*)

(ns-resolve *ns* (symbol "String"))
java.lang.String
В случае успеха ns-resolve возвращает экземпляр класса java.lang.Class.

Если конструктор класса с параметрами, необходимо найти конструктор с подходящей сигнатурой(метод getConstructor класса java.lang.reflect.Constructor). И создать новый объект методом newInstance.
Окончательно функция будет выглядеть так:

(defn new-instance [name & args]
  (let [class (ns-resolve *ns* (symbol name))]
    (if (= java.lang.Class (type class))
      (if args
        (let [type-args (into-array java.lang.Class (map #(type %) args))]
          (.newInstance (.getConstructor class type-args) (into-array args)))
        (.newInstance class)))))
Пример использования

user=> (new-instance "String")
""
user=> (new-instance "String" "abc")
"abc"
user=> (type (new-instance "String" "123"))
java.lang.String




суббота, 21 мая 2011 г.

Модификация атрибутов объекта в Clojure

Часто необходимо модифицировать состояние объекта, особенно когда работаешь с java библиотекой, смысл которой сводится к вызову callback'ов и необходимо сохранять промежуточное состояние между вызовами. Например SAX парсер.

Clojure определяет три способа работы с изменяемыми данными.  Это ссылки, агенты и атомы. Все они описаны в статье Алексея Отта (http://alexott.net/ru/clojure/clojure-intro/).
Для моего случая подходит синхронное изменение и заворачивать обращения  к изменяемым переменным в транзакции нет необходимости. Поэтому я буду использовать атомы(atoms).

Для примера опишем класс com.Example. Объявим одновременно пространство имен и класс(com.Example) с помощью макроса ns.

(ns com.Example
  (:gen-class
    :state state
    :init init
    :constructors {[] []}
    :methods [[setAttr [clojure.lang.Keyword int] void],
              [getAttr [clojure.lang.Keyword] int],
              [removeAttr [clojure.lang.Keyword] void]]
    :main false))

:init - имя конструктора
:state - имя атрибута для хранения состояния объекта
:constructors - описание типов параметров конструкторов класса
:methods - описание доступных методов класса, в формате [имя [тип_параметра ...] тип_возвращаемого_значения]
:main - false - не генерировать, автоматически, метод main(первый метод который будет вызван для java программы)

Теперь код объявленных выше методов.

Конструктор:

(defn -init []
[[] (atom {})])

конструктор без параметров. Конструктор класса в clojure должен вернуть вектор с двумя элементами.

[ [superclass-constructor-args] state]

Первый элемент -  еще один вектор, содержит параметры конструктора родительского класса([] - для родительского конструктора без параметров). Второй элемент - значение переменной состояния(имя которой определяется через параметр :state). В нашем случае это хеш(maps). Применение функции atoms делает состояние объекта изменяемым.

После того как state в классе объявлен как изменяемый его можно модифицировать функцией swap!.

Атрибуты объекта будут хранится как ключевые слова(keywords) а значения - это целые числа.
Методы для сохранения и получения значения атрибутов объекта будут такими:


(defn -setAttr [this attr value]
  (swap! (.state this) assoc attr value))

(defn -getAttr [this attr]
  (get @(.state this) attr))

(defn -removeAttr [this attr]
  (swap! (.state this) dissoc attr))


И маленький пример использования
user=> (def ex (com.Example.))
#'user/ex
user=> (.state ex)
#<Atom@724eb043: {}>
user=> ex
#<Example com.Example@30c06258>
user=> (.setAttr ex :a 1)
nil
user=> (.setAttr ex :b 2)
nil
user=> (.setAttr ex :super-key -9)  
nil
user=> (.state ex)
#<Atom@724eb043: {:super-key -9, :b 2, :a 1}>
user=> (.getAttr ex :b)
2
user=> (.removeAttr ex :super-key)
nil
user=> (.state ex)                
#<Atom@724eb043: {:b 2, :a 1}>

четверг, 28 апреля 2011 г.

Запуск одного Activity из другого

Для того что бы в android приложении запустить одно Activity из другого(например для навигации по интерфейсу приложения) нужно воспользоваться механизмом межпроцессной коммуникации.  Для передачи данных между процессами служит объект класса Intent. Для запроса activity, описанного в объекте класса Intent, служат методы startActivity и startActivityForResult.
Существует несколько конструкторов для создания объекта Intent. Если говорить про запуск Activity для решения задач навигации по интерфейсу(собственных интерфейсов), то можно использовать конструктор вида

 public Intent (Context packageContext, Class<?> cls)

с жестким заданием имени класса. Так же для запуска activity понадобится описать в файле AndroidManifest.xml вызываемую activity.

<activity android:name="com.example.MyExampleActivity"
android:label="@string/app_name" />

И запустить activity вызовом метода startActivity.

Код на Java для запроса activity может быть записать так:

this.startActivity(
    new Intent(this, com.example.MyExampleActivity.class)
);

На clojure запуск одной Activity из другой будет таким:

(.startActivity this (Intent. this com.example.MyExampleActivity)

this - текущая activity