Pull to refresh

Comments 18

Сумбурно и Codable не в тему тут, но zip(points, points.dropFirst()) красиво.
Согласна, статья получилась немного затянутой, но хотелось рассказать что-то более интересное, чем просто получение четных чисел с помощью filter(_:) или суммы чисел с помощью reduce(_:,_;).
Что касается Codable, то это АНТИпример того, куда может «завести» функциональный подход, например, для парсинга JSON. К сожалению, я не привела код, но это что-то типа:

blog <*> int(dict,"id")
<*> string(dict,"name")
<*> bool(dict,"needspassword")
<*> (string(dict, "url") >>= toURL


Самостоятельно введенные операторы «композиции» функций сильно грузят систему вывода ТИПА из контекста (inference) и очень затягивают время компиляции.
Насколько же было гениальмым решение Codable по сравнению с функциональным подходом.
Я всегда имею ввиду этот пример при выборе решения в пользу протоколов.
Поразительно! Поразительно, насколько восприятие у людей разное.

Знаете, что я скажу по поводу этой статьи? Великолепно! Превосходный стиль изложения, четкие формулировки, мысль донесена компактно и лаконично, совершенно нет воды, при этом по ходу изложения делаются краткие и абсолютно уместные «лирические отступления», обращающие внимание на какие-то особенности представленного решения. У меня по ходу чтения возникали «побочные» вопросы, ответы на которые давались буквально в следующей фразе и предложении, я даже не успевал этими вопросами озаботиться.

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

Да у меня мурашки иногда по коже бежали от удовольствия от прочтения!

«Сумбурно»… скажете тоже.

Зачем здесь escaping?


func map<T>(_ transform: @escaping (Element) -> T) -> [T]

ведь мы всегда клоужер сразу используем чтобы забрать значение? Разве нет?

Одна из причин использования @escaping — асинхронное выполнение кода. Если вы используете замыкание асинхронно с помощью DispatchQueгу, то очередь queue будет удерживать ваше замыкание в памяти для того, чтобы использовать его в будущем. В этом случае вы не можете точно сказать, когда замыкание будет выполнено. Все Generic map имеют @escaping замыкание, так как заранее неизвестно в какой среде вы будете его использовать.

У меня немного другая информация) в приведенном методе map вы в цикле бежите по массиву и добававляете значения в него сразу (до возвращения returnValue) то есть @escaping нам здесь не нужен

В стандартной библиотеке Apple map для Sequence сейчас действительно не имеет @escaping (хотя раньше имела):

func map(_ transform: (Element) throws -> T) rethrows -> [T]

Теперь map
перевыбрасывает (rethrows) ошибку.
Но во всех постах очень известного сайта по функциональному программированию на Swift pointfree.com во всех map перед transform стоит @escaping.
Но, похоже, вы правы и наличие @escaping сейчас является артефактом с прошлой версии.
Исправим.
var images = [6, 22, 8, 14, 16, 0, 7, 9]
var removedIndexes = [2,5,0,6]
var images1 = images
.enumerated()
.filter { !removedIndexes.contains($0.offset) }
.map { $0.element }
print (images1) // [22, 14, 16, 9]


По условиям задачи мы должны были получить массив [22,8,14,16,7,9] или я что-то не так понял?
По условиям задачи из массива [6, 22, 8, 14, 16, 0, 7, 9]:
— удаляется элемент с индексом 2, то есть 8
— удаляется элемент с индексом 5, то есть 0
— удаляется элемент с индексом 0, то есть 6
— удаляется элемент с индексом 6, то есть 7
И остается [22, 14, 16, 9]
Было восемь элементов, удалили четыре — осталось четыре
Считает все правильно — посмотрите на Playground.
Понял, спасибо. Я то подумал что у нас оба массива содержать id картинок.
UFO just landed and posted this here
Конечно, в императивном коде вы можете каждый раз создавать новый массив. Даже более того, если вы посмотрите на все эти map, flatMap, filter , то увидите, что именно так они и реализованы, никакой магии тут нет.
Но если вы находитесь в пределах императивного кода, то вам самому нужно все время контролировать себя, не делаете ли вы каких-то изменений «по месту».
Использование же функционального кода вам точно гарантирует «неизменяемость» за счет создания новых массивов ( или последовательностей или еще чего-нибудь) в функциях «высшего порядка».
Как раз единственный вопрос, который у меня остался — по поводу этого фрагмента:

Что произойдет, если другой поток (thread) попытается получить доступ к массиву numbers во время выполнения вашего кода?

Вопрос такой: а что произойдет, если другой поток попытается выполнить эту функцию? Ведь внутри нее массив output, который изменяется в процессе ее выполнения.

var output = [Int]()
        
for num in self {
    output.append(num * 10)
}
В Swift массивы Array являются структурами struct и Value ТИПАМИ. В Swift Value ТИПЫ хранятся в стеке.
Каждый поток имеет свое собственное пространство стека, поэтому никакой другой поток не сможет напрямую получить доступ к вашему Value ТИПУ. Следовательно, нет условий для гонки, блокировок, взаимных блокировок или каких-то других сложностей, связанных с синхронизацией потоков.

Если массив Array — очень большой, то для них использует механизм «Copy-on-Write».
Когда вы изменяете (например, добавляете append элементы) массив Array, он использует стандартную библиотечную функцию «isUniquelyReferenced», чтобы увидеть, есть ли какие-либо другие массивы, указывающие на буфер, к которому он собирается добавить. Если они есть, он делает копию, и копия становится ее новым резервным хранилищем. Это называется «Copy-on-Write».

Не правда. Struct может храниться в стеке, если удалось его во время compiler timer проанализировать и доказать, что значение не убежит. Value Type и Ref Type - имеют концептуальную разницу и не зависят от того, где что хранится.

Что значит "Если массив Array — очень большой" ? Standart Library всегда юзает copy on write для коллекций. Сам динамичный массив будет всегда в куче лежать.

Пожалуй, действительно не стоит обсуждать эффективное хранение структур structтем более в Standard Library.

В документации по Swift вовсе не сказано напрямую, что оптимизация хранения структур struct типа массивов Array, словарей Dictionary и строк String предполагает использование "кучи" (heap), там лишь сказано, что используется оптимизация для уменьшения затрат на копирование. Вместо того, чтобы делать копию немедленно, эти коллекции совместно используют память, в которой хранятся элементы, между исходным экземпляром и любыми копиями. Если одна из копий коллекции изменяется, элементы копируются непосредственно перед изменением.

Так что Apple оставляет за собой право, как она будет оптимизировать хранение структур struct, может и изменить это в любой момент времени.

Для нас важно лишь то, что в нашем коде ВСЕГДА у структуры struct будет такое поведение при изменении, как если бы копия была создана немедленно.

По большому счету структуры structявляются "неизменяемыми" (immutable) структурами данных в отличие от классов class.

Именно на основе того, что при любом изменении структуры structмы всегда имеем её новый экземпляр,и строится функциональное программирование.

Это особенно видно в новом фреймворке SwiftUI, в котором архитектура строится на основе MVVM, где View - всегда структуры struct, а ViewModels - классы class. Программирование в SwiftUI - это функциональное программирование.

Sign up to leave a comment.

Articles