Реализовать drop (n) и take (n) в Swift

#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 , который вы предоставляете нижнему индексу.