2015-08-16 4 views
5

Я хотел бы создать общий поиск(), обычно используемый в функциональном программировании. В функциональном программировании вы не работаете с индексами массива и для циклов. Вы фильтруете. Как это работает в том, что если у вас есть список сказатьfind() с использованием функционального программирования

["apple", "banana", "cherry"] 

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

[(1, "apple"), (2, "banana"), (3, "cherry")] 

Теперь вы можете фильтровать до «банана» и вернуть значение индекса.

Я пытался создать для этого общую функцию, но я получаю сообщение об ошибке. Что случилось с этим синтаксисом?

func findInGenericIndexedList<T>(indexedList: [(index: Int, value: T)], element: (index: Int, value: T)) -> Int? {   
    let found = indexedList.filter { // ERROR: Cannot invoke 'filter' with an argument list of type '((_) -> _)' 

     element.value === $0.value 
    } 

    if let definiteFound = found.first { 
     return definiteFound.index 
    } 
    return nil 
} 

UPDATE 1: Я хотел бы использовать вышеуказанное решение в отличие от использования находки() (будет устаревшее) или в Swift 2.0 IndexOf(), потому что я пытаюсь следовать функциональной парадигме программирования , опираясь на общие функции, а не на методы класса.

+0

1) В 'element === $ 0.value' вы сравниваете кортеж типа' (Int, T) 'со значением типа' T'. 2) '===', оператор «ident-to» определен только для экземпляров * classes *. 3) Знаете ли вы, что у Swift уже есть функция 'find'? (В Swift 2 он называется 'indexOf'.) –

+1

Спасибо за советы! Я обновил свой вопрос, почему find() indexOf() не являются моими предпочтительными решениями. Я обновил код на основе вашей точки 1. Это имеет смысл, однако оно все еще не работает. Если оператор идентичности '===' не работает в таких случаях, как бы вы пишете такую ​​общую функцию? – Daniel

ответ

2

Минимальное изменение, необходимое для выполнения этой работы, заключалось в том, чтобы сделать T соответствием Equatable и использовать оператор ==.

func findInGenericIndexedList<T:Equatable>(indexedList: [(index: Int, value: T)], element: (index: Int, value: T)) -> Int? { 
    let found = indexedList.filter { 
     element.value == $0.value 
    } 

    if let definiteFound = found.first { 
     return definiteFound.index 
    } 
    return nil 
} 

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

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

extension Array where Element : Equatable { 
    func find(element:Array.Generator.Element) -> Int? { 
     let indexedList = lazy(self.enumerate()) 
     let found = indexedList.filter { 
      element == $1 
     } 

     let definiteFound = found.prefix(1) 
     return definiteFound.generate().next()?.index 
    } 
} 

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

+0

Спасибо за подробный ответ. Я бы предпочел использовать оператор идентификации '===', потому что в моем массиве я могу иметь несколько элементов одного значения. Однако я хочу удалить точный элемент, который я передал функции. Это невозможно сделать? (Мне действительно нравится ваше расширение, но это может быть менее функциональное программирование и немного больше OO. FP работает больше с функциями более высокого порядка. Все передано.) – Daniel

+0

Как вы определяете «точный элемент»? Является ли элемент с тем же индексом и тем же контентом? I.e., где (Int, String) кортеж будет одним и тем же? Или вы ожидаете наличия нескольких кортежей с точно таким же контентом? –

+0

@Roman Ну, очевидно, вы уже не знаете индекс. В противном случае вы могли бы просто вернуть это. –

1

Вот пара мыслей.

Я бы предпочел использовать оператор идентификации '===', потому что в моем массиве я могу иметь несколько элементов одного и того же значения.

Оператор идентификации === работает только для ссылочных типов, таких как классы. Он никогда не будет работать для типов значений, таких как String s или Int s, или struct s и т. Д. Вы можете взглянуть на разницу между типами значений и ссылочными типами, особенно если вы заинтересованы в функциональном программировании, которое почти избегает ссылочных типов полностью. Когда вы работаете со значениями типов, существует только равенство (==) - идентификация отсутствует. Два экземпляра String «bananas» никогда не будут ссылаться на один и тот же объект. Они всегда будут относиться к двум различным String s, хотя их значения могут быть равным.

Я хочу удалить точный элемент, который я передал функции. Это невозможно сделать?

Если вы работаете со значениями типа, например String s, то да, это невозможно.Нет такой вещи, как два разных String s, которые являются одним и тем же предметом. Два String s всегда всегда разные объекты по причинам, указанным выше.

Обратите внимание, что если вы работаете только с классами, а не с типами значений, то вы можете использовать оператор ===, но это приведет к победе над тем, что вы пытаетесь сделать.

Что это сводится к тому, что если у вас есть массив (index, value) кортежей, выглядит следующим образом:

[(0, "bananas"), (1, "apples"), (2, "oranges"), (3, "bananas")] 

И вы пишете функцию, которая ищет кортежи, где значение является "bananas", у вас есть пара выбора. Вы можете отфильтровать его и найти первый tuple в массиве, который имеет значение "bananas" и возвращает индекс этого кортежа. В приведенном выше случае он вернет 0. Или, вы можете вернуть все индексов в виде массива, например: [0, 3]. Или, я полагаю, вы могли бы вернуть какое-то другое произвольное подмножество результатов, например, индекс , последний индекс, или индекс первых и последних и т. Д., Но все они кажутся немного глупыми. Стандартная библиотека Swift позволяет возвращать индекс первого элемента, который соответствует критериям поиска именно по этой причине. Ни один из других вариантов не имеет большого смысла.

Но вернув его в контексте вашего вопроса, ни один из кортежей, который вы найдете со значением "bananas", будет точным (идентичным) экземпляром "bananas", который вы передали в свою функцию поиска. Никакие два типа значений не идентичны. Они могут быть равными, но они никогда не идентичны.

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

                 // ----------vvvvv 
func findInGenericIndexedList<T>(indexedList: [(index: Int, value: T)], element: (index: Int, value: T)) -> Int? 

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

+0

Да, это опечатка. :) Я должен только передать элемент. Еще раз спасибо за подробный ответ. Я удивлен, что вы не можете найти идентичную ссылку. Примитивные типы, Строки, сложные типы, все они имеют ссылки на них. Даже когда это невозможно. Я согласен, что вы не можете этого сделать, но я до сих пор не понимаю, почему. Почему я не могу проверить, имеет ли объект/примитивный тип/любой тип ту же ссылку, что и «Я держусь в руке» (т. Е. Передан как параметр)? – Daniel

+0

Это особенность языка Swift. Типы значений более безопасны, поскольку изменение одного экземпляра не изменяет каждый другой экземпляр, на который ссылается. Оба типа значений и ссылочные типы ценны по-своему. –

+0

Это имеет смысл. Согласовано. – Daniel

Смежные вопросы