Превратите этот вызов Clojure в ленивую последовательность

StackOverflow https://stackoverflow.com/questions/1628412

Вопрос

Я работаю с набором инструментов для обмена сообщениями (это распространение , но я этого не знаю детали имеют значение). Для получения сообщений из этого инструментария необходим некоторый шаблон:

<Ол>
  • Создайте соединение с демоном.
  • Присоединиться к группе.
  • Получите одно или несколько сообщений.
  • Выйдите из группы.
  • Отключиться от демона.
  • Следуя некоторым идиомам, которые я видел, использовал в другом месте , я смог приготовить некоторые рабочие функции, использующие Java API Spread и формы взаимодействия Clojure:

    (defn connect-to-daemon
      "Open a connection"
      [daemon-spec]
      (let [connection (SpreadConnection.)
            {:keys [host port user]} daemon-spec]
        (doto connection
          (.connect (InetAddress/getByName host) port user false false))))
    
    (defn join-group
      "Join a group on a connection"
      [cxn group-name]
      (doto (SpreadGroup.)
        (.join cxn group-name)))
    
    (defn with-daemon*
      "Execute a function with a connection to the specified daemon"
      [daemon-spec func]
      (let [daemon (merge *spread-daemon* daemon-spec)
            cxn (connect-to-daemon daemon-spec)]
        (try
         (binding [*spread-daemon* (assoc daemon :connection cxn)]
           (func))
         (finally
          (.disconnect cxn)))))
    
    (defn with-group*
      "Execute a function while joined to a group"
      [group-name func]
      (let [cxn (:connection *spread-daemon*)
            grp (join-group cxn group-name)]
        (try
         (binding [*spread-group* grp]
           (func))
         (finally
          (.leave grp)))))
    
    (defn receive-message
      "Receive a single message. If none are available, this will block indefinitely."
      []
      (let [cxn (:connection *spread-daemon*)]
        (.receive cxn)))
    

    (в основном та же идиома, что и with-open , просто класс SpreadConnection использует disconnect вместо close . Grr. Также я пропустил некоторые макросы, которые не имеют отношения к структурному вопросу здесь.)

    Это работает достаточно хорошо. Я могу вызвать Receive-сообщение из структуры, например:

    (with-daemon {:host "localhost" :port 4803}
      (with-group "aGroup"
        (... looping ...
          (let [msg (receive-message)] 
            ...))))
    

    Мне пришло в голову, что receive-message было бы удобнее использовать, если бы это была бесконечная ленивая последовательность, которая создает сообщения. Итак, если я хочу присоединиться к группе и получать сообщения, код вызова должен выглядеть примерно так:

    (def message-seq (messages-from {:host "localhost" :port 4803} "aGroup"))
    (take 5 message-seq)
    

    Я видел множество примеров ленивых последовательностей без очистки, это не так уж сложно. Подвох - шаги 4 и 5 сверху: выход из группы и отключение от демона. Как связать состояние соединения и группы в последовательность и запустить необходимый код очистки, когда последовательность больше не нужна?

    Это было полезно?

    Решение

    Эта статья описывает как это сделать, используя clojure-contrib fill-queue. Относительно очистки - полезная вещь в fill-queue заключается в том, что вы можете предоставить блокирующую функцию, которая очищает себя в случае ошибки или какого-либо условия. Вы также можете держать ссылку на ресурс, чтобы управлять им извне. Последовательность просто прекратится. Таким образом, в зависимости от вашего семантического требования, вам придется выбрать подходящую стратегию.

    Другие советы

    Попробуйте это:

    (ns your-namespace
      (:use clojure.contrib.seq-utils))
    
    (defn messages-from [daemon-spec group-name]
      (let [cnx (connect-to-deamon daemon-spec))
            group (connect-to-group cnx group-name)]
        (fill-queue (fn [fill]
                      (if done? 
                          (do
                            (.leave group)
                            (.disconnect cnx)
                            (throw (RuntimeException. "Finished messages"))
                          (fill (.receive cnx))))))
    

    Установить готово? true, когда вы хотите завершить список. Кроме того, любые исключения (.receive cnx) также будут завершать список.

    Лицензировано под: CC-BY-SA с атрибуция
    Не связан с StackOverflow
    scroll top