Мышление в ленивых последовательностях

Кажется, что VISIBILITY_SECRET делает самый чистый подход. В соответствии с документацией:

может быть сделано уведомление VISIBILITY_SECRET, которое будет подавлять его значок и тикер до тех пор, пока пользователь не обойдет блокирующий экран.

blockquote>

source (NotificationData в проекте SystemUI AOSP), VISIBILITY_SECRET - единственный способ сделать это:

boolean shouldFilterOut(StatusBarNotification sbn) {
    if (!(mEnvironment.isDeviceProvisioned() ||
            showNotificationEvenIfUnprovisioned(sbn))) {
        return true;
    }

    if (!mEnvironment.isNotificationForCurrentProfiles(sbn)) {
        return true;
    }

    if (sbn.getNotification().visibility == Notification.VISIBILITY_SECRET &&
            mEnvironment.shouldHideSensitiveContents(sbn.getUserId())) {
        return true;
    }
    return false;
}

Единственный другой тип уведомлений, которые, как представляется, отфильтровываются, - это дочерние уведомления в группе, где сводка настоящее. Поэтому, если у вас нет множественного числа с обоснованной причиной резюме, VISIBILITY_SECRET - это лучшее, что можно сделать в настоящее время.

20
задан Arun R 6 February 2010 в 16:35
поделиться

3 ответа

Как вы отметили, [0 1] устанавливает базовые случаи: первые два значения в последовательности равны нулю, затем одно . После этого каждое значение должно быть суммой предыдущего значения и значения перед этим. Следовательно, мы не можем даже вычислить третье значение в последовательности, если перед ним не будет по крайней мере два. Вот почему нам нужны два значения для начала.

Теперь посмотрим на форму map . Он говорит, что нужно взять элементы заголовка из двух разных последовательностей, объединить их с помощью функции + (сложить несколько значений для получения одной суммы) и предоставить результат как следующее значение в последовательности. Форма map объединяет две последовательности - предположительно одинаковой длины - в одну последовательность одинаковой длины.

Две последовательности, загруженные в карту , представляют собой разные представления одной и той же базовой последовательности, сдвинутой на один элемент. Первая последовательность - это «все, кроме первого значения базовой последовательности». Вторая последовательность - это сама базовая последовательность, которая, конечно же, включает первое значение. Но какой должна быть базовая последовательность?

В приведенном выше определении сказано, что каждый новый элемент является суммой предыдущего ( Z - 1 ) и предшественника предыдущего элемента ( Z - 2 ). Это означает, что для расширения последовательности значений требуется доступ к ранее вычисленным значениям в той же последовательности. Нам определенно нужен двухэлементный регистр сдвига , но вместо этого мы также можем запросить доступ к нашим предыдущим результатам.Вот что здесь делает рекурсивная ссылка на последовательность fib-seq . Символ fib-seq относится к последовательности, которая представляет собой конкатенацию нуля, единицы, а затем суммы собственных значений Z - 2 и Z - 1 . .

Взяв последовательность под названием fib-seq , отрисовка первого элемента дает первый элемент вектора [0 1] - ноль. Рисование второго элемента дает второй элемент вектора - один. После рисования третьего элемента мы обращаемся к карте , чтобы сгенерировать последовательность и использовать ее в качестве оставшихся значений. Последовательность, сгенерированная map здесь, начинается с суммы первого элемента «остальной части» [0 1] , который равен единице, и первого элемента [0 1] , что равно нулю. Эта сумма равна единице.

Рисование четвертого элемента снова обращается к карте , которая теперь должна вычислить сумму второго элемента «остальной части» базовой последовательности, которая генерируется картой , и второй элемент базовой последовательности, который является элементом вектора [0 1] . Эта сумма равна двум.

Рисование пятого элемента обращается к карте , суммируя третий элемент «остальной части» базовой последовательности - опять же, результат суммирования нуля и единицы - и третий элемент базовой последовательности - которых мы только что обнаружили, чтобы их было двое.

Вы можете видеть, как это строится, чтобы соответствовать предполагаемому определению для сериала.Сложнее увидеть, повторяет ли рисование каждого элемента все предыдущие значения дважды - по одному разу для каждой последовательности, проверенной с помощью map . Оказывается, здесь нет такого повторения.

Чтобы подтвердить это, дополним определение fib-seq , как это, чтобы использовать функцию + :

(def fib-seq
  (lazy-cat [0 1] 
            (map 
              (fn [a b]
                (println (format "Adding %d and %d." a b))
                (+ a b))
            (rest fib-seq) fib-seq)))

Теперь запросите первые десять элементов:

> (doall (take 10 fib-seq))
Adding 1 and 0.
Adding 1 and 1.
Adding 2 and 1.
Adding 3 and 2.
Adding 5 and 3.
Adding 8 and 5.
Adding 13 and 8.
Adding 21 and 13.
(0 1 1 2 3 5 8 13 21 34)

Обратите внимание, что есть восемь вызовов + для генерации первые десять значений.


С момента написания предыдущего обсуждения я потратил некоторое время на изучение реализации ленивых последовательностей в Clojure - в частности, файла LazySeq.java - и подумал, что это хорошее место, чтобы поделиться несколько наблюдений.

Во-первых, обратите внимание, что многие функции отложенной обработки последовательностей в Clojure в конечном итоге используют lazy-seq вместо некоторой другой коллекции. lazy-seq создает экземпляр типа Java LazySeq , который моделирует небольшой конечный автомат. У него есть несколько конструкторов, которые позволяют ему запускаться в разных состояниях, но наиболее интересным случаем является тот, который начинается просто со ссылки на нулевую функцию. Созданный таким образом LazySeq не оценивал функцию и не находил последовательность делегата (тип ISeq в Java). При первом запросе у LazySeq его первого элемента - через first - или любых последующих - через next или rest - он оценивает функция, копается в получившемся объекте, чтобы удалить все слои оболочки других экземпляров LazySeq , и, наконец, передает самый внутренний объект через java-функцию RT # seq () , что приводит к в экземпляре ISeq .

На данный момент LazySeq имеет ISeq , которому можно делегировать вызовы от имени сначала , затем и остальное . Обычно "голова" ISeq будет иметь тип Cons , который хранит постоянное значение в своем "первом" (или "автомобильном") слоте и другом ISeq в слоте "rest" (или "cdr"). Этот ISeq в своем слоте "rest", в свою очередь, может быть LazySeq , и в этом случае для доступа к нему снова потребуется такая же оценка функции, удаляя любые ленивые оболочки при возврате. значение и передача этого значения через RT # seq () , чтобы получить еще один ISeq , которому следует делегировать.

Экземпляры LazySeq остаются связанными вместе, но с принудительно один (через сначала , затем или отдых ) заставляет его делегировать сразу несколько неленивых ISeq после этого. Обычно это форсирование оценивает функцию, которая возвращает Cons , привязанную к первому значению, а ее хвост - к другому LazySeq ; это цепочка функций генератора, каждая из которых дает одно значение («первый» слот Cons ), связанное с другой возможностью для получения большего количества значений ( LazySeq в Cons Слот для отдыха ).

Связывая это, в приведенном выше примере последовательности Фибоначчи, карта будет брать каждую из вложенных ссылок на fib-seq и проходить их отдельно с помощью повторных вызовов отдых . Каждый такой вызов преобразует не более одного LazySeq , содержащего неоцененную функцию, в LazySeq , указывающий на что-то вроде Cons . После преобразования любые последующие обращения быстро разрешатся в Cons es - где хранятся фактические значения. Когда одна ветвь map ziping проходит fib-seq один элемент за другим, значения уже определены и доступны для постоянного доступа без дальнейшей оценки требуется функция генератора.

Вот несколько диаграмм, которые помогут визуализировать эту интерпретацию кода:

        +---------+
        | LazySeq |
fib-seq |  fn -------> (fn ...)
        |  sv     |
        |  s      |
        +---------+


        +---------+
        | LazySeq |
fib-seq |  fn -------> (fn ...) -+
        |  sv <------------------+
        |  s      |
        +---------+


        +---------+
        | LazySeq |
fib-seq |  fn     |
        |  sv -------> RT#seq() -+
        |  s  <------------------+
        +---------+


        +---------+   +------+
        | LazySeq |   | ISeq |
fib-seq |  fn     |   |      |
        |  sv     |   |      |
        |  s  ------->|      |
        +---------+   +------+


        +---------+   +--------+        +------+
        | LazySeq |   | Cons   |        | ISeq |
fib-seq |  fn     |   |  first ---> 1   |      |
        |  sv     |   |  more  -------->|      |
        |  s  ------->|        |        |      |
        +---------+   +--------+        +------+


        +---------+   +--------+        +---------+
        | LazySeq |   | Cons   |        | LazySeq |
fib-seq |  fn     |   |  first ---> 1   |  fn -------> (fn ...)
        |  sv     |   |  more  -------->|  sv     |
        |  s  ------->|        |        |  s      |
        +---------+   +--------+        +---------+

По мере продвижения map она перескакивает с LazySeq на LazySeq (и, следовательно, Минусы - Минусы ), а крайний правый край расширяется только при первом вызове сначала , затем , или rest на заданном LazySeq .

37
ответ дан 29 November 2019 в 23:11
поделиться

Что касается того, как это работает:

Каждый член ряда Фибоначчи, очевидно, является результатом сложения двух предыдущих членов. Это то, что здесь делает map, map применяет + к каждому элементу в каждой последовательности, пока не закончится одна из последовательностей (чего, конечно, не будет в данном случае). Таким образом, результатом является последовательность чисел, которая получается в результате добавления одного члена в последовательности к следующему члену в последовательности. Затем вам понадобится lazy-cat, чтобы дать ему отправную точку и убедиться, что функция возвращает только то, о чем она просила.

Проблема с этой реализацией заключается в том, что fib-seq удерживает всю последовательность, пока определен fib-seq, поэтому в конечном итоге у вас будет нехватка памяти.

В книге Стюарта Хэллоуэя некоторое время тратится на анализ различных реализаций этой функции, я думаю, что наиболее интересная из них приведена ниже (это книга Кристофа Гранде):

(defn fibo []
    (map first (iterate (fn [[a b]] [b (+ a b)]) [0 1])))

В отличие от опубликованной реализации, ранее прочитанные элементы последовательности не имеют ничего общего с ними, поэтому этот может продолжать работать, не генерируя OutOfMemoryError.

Как заставить думать в этих терминах - более сложный вопрос. Пока для меня это вопрос знакомства с множеством различных способов выполнения задач и их опробования, в то время как в целом я ищу способы применить существующую библиотеку функций вместо использования рекурсии и ленивого кота. Но в некоторых случаях рекурсивное решение действительно отличное, поэтому это зависит от проблемы. Я с нетерпением жду книги Joy of Clojure , потому что я думаю, что она мне очень поможет в решении этой проблемы.

3
ответ дан 29 November 2019 в 23:11
поделиться

Мой Clojure немного ржавый, но, похоже, это дословный перевод знаменитого однострочника Haskell:

fibs = 0 : 1 : zipWith (+) fibs (tail fibs)
{{1 }}

[Я собираюсь использовать псевдо-Haskell, потому что он немного более лаконичен.]

Первое, что вам нужно сделать, это просто позволить лени погрузиться в себя. Когда вы смотрите на определение вот так:

zeroes = 0 : zeroes

Ваша непосредственная внутренняя реакция как строгого программиста будет: «Бесконечный цикл ZOMG! Необходимо исправить ошибку!» Но это не бесконечный цикл. Это ленивый бесконечный цикл. Если вы сделаете что-нибудь глупое, например

print zeroes

, тогда да, будет бесконечным циклом. Но пока вы просто используете конечное число элементов, вы никогда не заметите, что рекурсия на самом деле не завершается. Это действительно трудно достать. Я все еще не знаю.

Лень похожа на денежную систему: она основана на предположении, что подавляющее большинство людей никогда не использует подавляющее большинство своих денег. Итак, когда вы кладете 1000 долларов в банк, они не хранят их в своем сейфе. Они одалживают его кому-то другому. Фактически, они используют деньги, что означает, что они ссужают 5000 долларов кому-то другому. Им всегда нужно достаточно денег, чтобы они могли быстро перетасовать их так, чтобы они были там , когда вы смотрите на них, создавая впечатление , что они действительно хранят ваши деньги.

Пока им удается всегда выдавать деньги, когда вы подходите к банкомату, на самом деле не имеет значения, что подавляющего большинства ваших денег там нет: им нужна только небольшая сумма, в которой вы снимаете момент времени, когда вы снимаете деньги.

Лень работает так же: всякий раз, когда вы смотрите на нее, вы видите ценность. Если вы посмотрите на первый, десятый, сотый, квадриллионный элемент нулей , он будет там . Но он будет только , если и , когда вы посмотрите на него, не раньше.

Вот почему это безупречно рекурсивное определение нулей работает: пока вы не пытаетесь просмотреть последний элемент (или каждый элемент) бесконечного списка, вы в безопасности.

Следующий шаг - zipWith . (Clojure map - это просто обобщение того, что в других языках программирования обычно представляет собой три разные функции: map , zip и zipWith . в этом примере он используется как zipWith .)

Причина, по которой семейство функций zip называется таким образом, заключается в том, что оно фактически работает как застежка-молния, а именно также как лучше всего это визуализировать. Допустим, у нас есть какое-то спортивное мероприятие, где каждый участник получает по две попытки, и результат обеих попыток складывается для получения конечного результата.Если у нас есть две последовательности, run1 и run2 с оценками каждого прогона, мы можем вычислить конечный результат следующим образом:

res = zipWith (+) run1 run2

Предполагая, что наши два списка - (3 1 6 8 6) и (4 6 7 1 3) , мы выстраиваем оба этих списка бок о бок, как две половинки молнии, а затем застегиваем их вместе, используя нашу заданная функция (в данном случае + ) для получения новой последовательности:

3   1   6   8   6
+   +   +   +   +
4   6   7   1   3
=   =   =   =   =
7   7  13   9   9

Победитель №3.

Итак, как выглядит наша fib ?

Ну, она начинается с 0 , затем мы добавляем 1 , затем мы добавить сумму бесконечного списка с бесконечным списком, сдвинутым на один элемент. Проще всего это нарисовать:

  • первый элемент равен нулю:

     0 
     
  • второй элемент равен единице:

     0 1 
     
  • третий element - это первый элемент плюс первый элемент остальных (то есть второй элемент). Мы снова визуализируем это как застежку-молнию, помещая два списка друг на друга.

     0 1 
     + 
    1 
     = 
    1 
     
  • Итак, элемент, который мы только что вычислили, - это не просто output функции zipWith , это в то же время также вход , поскольку он добавляется к обоим спискам (которые фактически являются одним и тем же списком, только по одному):

     0 1 1 
     + + 
    1 1 
     = = 
    1 2 
     
  • и так далее:

     0 1 1 2 
     + + + 
    1 1 2 
     = = = 
    1 2 3 
     
     { {1}} 0 1 1 2 3 ^ 
     + + + + | 
    1 1 2 3 ^ | 
     = = = = | | 
    1 2 3 5 --- + --- + 
     

Или, если вы нарисуете его немного по-другому и объедините список результатов и второй список входных данных (которые на самом деле являются то же самое, во всяком случае) в один:

0   1   1   2   3   ^
+   +   +   +   +   |
1 = 1 = 2 = 3 = 5---+

Вот как Я все равно это визуализирую.

12
ответ дан 29 November 2019 в 23:11
поделиться
Другие вопросы по тегам:

Похожие вопросы: