CompactMap на sequence () не ленивый? - PullRequest
2 голосов
/ 27 июня 2019

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

let chain = sequence(first: someView as UIResponder) {$0.next}

Это замечательно, потому что до сих пор мы на самом деле не ходили пешком; последовательность ленива, и анонимная функция не будет выполнена, пока мы не начнем запрашивать элементы. Чтобы доказать это, позвольте мне снабдить этот код инструкцией печати:

let chain = sequence(first: someView as UIResponder) {r in print(r); return r.next}

Хорошо, допустим, я ищу первый экземпляр ViewController в цепочке. Я могу найти это так:

if let vc = (chain.first {$0 is ViewController}) as? ViewController {
    print(vc)
}

Распечатка показывает, что лень сохраняется: мы шли по цепочке респондента, пока не добрались до ViewController и остановились. Отлично! Внутри фигурных скобок vc напечатано как ViewController, и мы отправляемся в гонки.

Это не ускользнуло от вашего внимания, однако, это ужасно. Я и тестирую и кастую. Есть ли способ, которым я могу просто привести без тестирования и все же получить ViewController?

Это элегантно и прекрасно работает:

for case let vc as ViewController in chain {
    print(vc)
    break
}

Это прекрасно, и лень сохраняется, - но я должен вспомнить, чтобы сказать break в конце, что все разрушает.

Хорошо, так что я очень надеялся, когда думал об этом:

if let vc = (chain.compactMap{ $0 as? ViewController }.first) {
    print(vc)
}

Он работает в том смысле, что он компилирует, получает правильный ответ и выглядит красиво, но я потерял лень . Весь chain пересекается. compactMap теряет лень? Есть ли способ вернуть его? (Или есть какой-то другой элегантный способ, который полностью ускользнул от меня?)

1 Ответ

1 голос
/ 27 июня 2019

Проблема не compactMap, как таковая.Есть две проблемы:

  1. Если вы хотите, чтобы последовательность вызывала compactMap лениво, вам нужно использовать lazy.

  2. Похоже, что first предотвращает ленивое поведение.Если вы используете first(where:), вы все равно наслаждаетесь ленивым поведением.

Таким образом, хотя и несколько не элегантно, следующее достигает того, что вы ищете:

if let vc = (chain.lazy.compactMap { $0 as? ViewController }.first { _ in true } ) {
    ...
} 

Или, как вы говорите, вы можете реализовать first (или lazyFirst) на Sequence:

extension Sequence {
    var first: Element? {
        return first { _ in true }
    }
}

И тогда это более упрощенное отображение по-прежнему лениво:

if let vc = chain.lazy.compactMap({ $0 as? ViewController }).first {
    ...
} 
...