#swift
#swift
Вопрос:
Я реализовал drop и take для массивов в swift. Желаемое поведение
let list = [1,2,3,4]
list.drop(1) // [2,3,4]
list.take(2) // [1,2]
list.drop[1].take[2] // [2,3]
Столкнулся с проблемой, когда подписанный массив является «срезом», поэтому, похоже, мне нужно расширить как массив, так и срез:
extension Slice {
func take(num: Int) -> Slice<T> {
let n = (num < self.count) ? num : self.count
return self[0..n]
}
func drop(num: Int) -> Slice<T> {
let n = (num < self.count) ? num : self.count
return self[n..self.count]
}
}
// Extend array to use the Slice extension
extension Array {
func take(num: Int) -> Slice<T> {
let slice = Slice(self)
return slice.take(num)
}
func drop(num: Int) -> Slice<T> {
let slice = Slice(self)
return slice.drop(num)
}
}
Это работает.
=> Есть ли лучший способ сделать это с помощью системы типов Swift?
Комментарии:
1. Я обнаружил, изучив свой ответ (и пытаясь реализовать
dropWhile
иtakeWhile
), что в Swift уже естьprefix
иsuffix
глобальные функции, которые делают это. Если вы посмотрите на их подписи, они немного более общие, чем мои.
Ответ №1:
Вы заметите, что многие функции в Swift, которые можно было бы ожидать от методов, на самом деле являются глобальными функциями, такими как contains
, first
, и так далее. Если вы хотите реализовать take
и drop
по-настоящему универсальным способом, вы должны следовать этой технике. Хитрость заключается в создании функций, которые работают со Sliceable
значениями. К сожалению, вы не можете написать расширение Sliceable
, потому что оно использует Self
и имеет связанные типы.
Итак, то, чего вы хотите достичь, должно выглядеть примерно так:
func take<T: Sliceable where T.Index == Int>(sliceable: T, end: Int) -> T.SubSlice {
return sliceable[0..<end]
}
func drop<T: Sliceable where T.Index == Int>(sliceable: T, start: Int) -> T.SubSlice {
return sliceable[start..<countElements(sliceable)]
}
Я потратил на это всего две минуты, хотя я провел некоторое элементарное тестирование, и они работают просто отлично.
Хотя я считаю, что это самое «общее» решение, которое вы можете получить в Swift, на мой взгляд, оно неоптимально. Преимущество, которое он имеет перед вашим, заключается в том, что он работает с любым Sliceable
типом, например String
. Недостатком является то, что синтаксис отстой. Вы не можете использовать с ним свободный стиль:
take(drop(array, 3), 7)
по сравнению с гораздо более плавным
array.drop(3).take(7)
Я думаю, что системе типов Swift предстоит пройти небольшой путь, прежде чем она станет оптимальной, но это улучшение по сравнению с Objective-C.
Обновить
С тех пор, как я написал это, я узнал о функциях Swift prefix
и suffix
глобальных функциях. Их следует использовать вместо этого.
Комментарии:
1. Должен быть принятый ответ —
prefix
иsuffix
это путь
Ответ №2:
Ваша реализация выглядит абсолютно нормально для меня! Нижний индекс на основе диапазона является Slice
типом из соображений производительности. Он не копирует указанный подмассив, вместо этого он просто указывает на диапазон, определенный тем Range
, который вы предоставляете нижнему индексу.