NSObject
уже соответствует протоколу Hashable
:
extension NSObject : Equatable, Hashable {
/// The hash value.
///
/// **Axiom:** `x == y` implies `x.hashValue == y.hashValue`
///
/// - Note: the hash value is not guaranteed to be stable across
/// different invocations of the same program. Do not persist the
/// hash value across program runs.
public var hashValue: Int { get }
}
public func ==(lhs: NSObject, rhs: NSObject) -> Bool
Я не мог найти официальную ссылку, но кажется, что hashValue
вызывает метод hash
из NSObjectProtocol
, и ==
вызывает метод isEqual:
(из того же протокола). См. Обновление в конце ответа!
Для подклассов NSObject
правильный способ, по-видимому, состоит в том, чтобы переопределить hash
и isEqual:
, и вот эксперимент, который показывает, что:
hashValue
и ==
class ClassA : NSObject {
let value : Int
init(value : Int) {
self.value = value
super.init()
}
override var hashValue : Int {
return value
}
}
func ==(lhs: ClassA, rhs: ClassA) -> Bool {
return lhs.value == rhs.value
}
Теперь создайте два разных экземпляра класса, которые считаются «равными» и помещают их в набор:
let a1 = ClassA(value: 13)
let a2 = ClassA(value: 13)
let nsSetA = NSSet(objects: a1, a2)
let swSetA = Set([a1, a2])
print(nsSetA.count) // 2
print(swSetA.count) // 2
As вы можете видеть, что оба NSSet
и Set
рассматривают объекты как разные. Это не желаемый результат. Массивы также имеют неожиданные результаты:
let nsArrayA = NSArray(object: a1)
let swArrayA = [a1]
print(nsArrayA.indexOfObject(a2)) // 9223372036854775807 == NSNotFound
print(swArrayA.indexOf(a2)) // nil
Установка контрольных точек или добавление вывода отладки показывает, что переопределенный оператор ==
никогда не вызывается. Я не знаю, является ли это ошибкой или предполагаемым поведением.
hash
и isEqual:
class ClassB : NSObject {
let value : Int
init(value : Int) {
self.value = value
super.init()
}
override var hash : Int {
return value
}
override func isEqual(object: AnyObject?) -> Bool {
if let other = object as? ClassB {
return self.value == other.value
} else {
return false
}
}
}
Для Swift 3 определение isEqual:
изменилось на
override func isEqual(_ object: Any?) -> Bool { ... }
Теперь все результаты ожидаются:
let b1 = ClassB(value: 13)
let b2 = ClassB(value: 13)
let nsSetB = NSSet(objects: b1, b2)
let swSetB = Set([b1, b2])
print(swSetB.count) // 1
print(nsSetB.count) // 1
let nsArrayB = NSArray(object: b1)
let swArrayB = [b1]
print(nsArrayB.indexOfObject(b2)) // 0
print(swArrayB.indexOf(b2)) // Optional(0)
Обновление: теперь это поведение описано в Взаимодействие с API-интерфейсом Objective-C в ссылке «Использование Swift с какао и Objective-C»:
Класс NSObject выполняет только сравнение идентичности, поэтому вы должны реализовать свой собственный метод isEqual: в классах, которые происходят из класса NSObject.
Как часть реализации равенства для вашего класса, обязательно реализуйте свойство хеша в соответствии с правилами сравнения объектов.
blockquote>
(Редактирование: маленькое Ocaml FP Koan для начинания вещей)
Koan Приправления карри (koan о еде которая не является о еде)
<блок цитирования>студент А приехал к Jacques Garrigue и сказал, "Я не понимаю то, для чего приправление карри хорошо". Jacques ответил, "Скажите меня Ваша любимая еда и Ваш любимый десерт". Озадаченный студент ответил, что ему понравились okonomiyaki и kanten, но в то время как его любимый ресторан обслуживал большой okonomiyaki, их kanten всегда давал ему боль в желудке следующим утром. Таким образом, Jacques взял студента для еды в ресторане, который подал okonomiyaki, столь же хороший как фаворит студента, затем взял его по городу в магазин, который сделанный превосходным kanten, где студент счастливо применил остаток от своего аппетита. Студент был пресыщен, но он не был просвещен... до следующего утра, когда он проснулся, и его живот чувствовал себя прекрасно.
блок цитирования>
Мои примеры покроют использование его для повторного использования и инкапсуляции кода. Это довольно очевидно, после того как Вы смотрите на них и должны дать Вам конкретный, простой пример, что можно думать о применении в многочисленных ситуациях.
Мы хотим сделать карту по дереву. Эта функция могла приправляться карри и относиться каждый узел, если бы требуется более затем один аргумент - так как мы применили бы тот в узле, поскольку это - заключительный аргумент. Это не должно быть приправлено карри, но запись другой функция (предполагающий, что эта функция используется в других случаях с другими переменными), были бы отходы.
type 'a tree = E of 'a | N of 'a * 'a tree * 'a tree
let rec tree_map f tree = match tree with
| N(x,left,right) -> N(f x, tree_map f left, tree_map f right)
| E(x) -> E(f x)
let sample_tree = N(1,E(3),E(4)
let multiply x y = x * y
let sample_tree2 = tree_map (multiply 3) sample_tree
, но это совпадает с:
let sample_tree2 = tree_map (fun x -> x * 3) sample_tree
, Таким образом, этот простой случай не убедителен. Это действительно - хотя, и мощный, после того как Вы используете язык больше и естественно сталкиваетесь с этими ситуациями. Другой пример с некоторым повторным использованием кода как приправление карри. рекуррентное соотношение для создания простых чисел . Куча подобия там:
let rec f_recurrence f a seed n =
match n with
| a -> seed
| _ -> let prev = f_recurrence f a seed (n-1) in
prev + (f n prev)
let rowland = f_recurrence gcd 1 7
let cloitre = f_recurrence lcm 1 1
let rowland_prime n = (rowland (n+1)) - (rowland n)
let cloitre_prime n = ((cloitre (n+1))/(cloitre n)) - 1
хорошо, теперь rowland и cloitre приправлены функции карри, так как у них есть свободные переменные, и мы можем получить любой индекс, он - последовательность, не зная или вызывая беспокойство о f_recurrence.
В то время как предыдущие примеры ответили на вопрос, вот два более простых примера того, как Приправление карри может быть выгодным для программирования F#.
open System.IO
let appendFile (fileName : string) (text : string) =
let file = new StreamWriter(fileName, true)
file.WriteLine(text)
file.Close()
// Call it normally
appendFile @"D:\Log.txt" "Processing Event X..."
// If you curry the function, you don't need to keep specifying the
// log file name.
let curriedAppendFile = appendFile @"D:\Log.txt"
// Adds data to "Log.txt"
curriedAppendFile "Processing Event Y..."
И не забывают, что можно приправить семейство Printf карри функции! В версии с приправой карри заметьте отличное отсутствие лямбды.
// Non curried, Prints 1 2 3
List.iter (fun i -> printf "%d " i) [1 .. 3];;
// Curried, Prints 1 2 3
List.iter (printfn "%d ") [1 .. 3];;
Это - довольно простой процесс. Возьмите функцию, свяжите один из ее аргументов и возвратите новую функцию. Например:
let concatStrings left right = left + right
let makeCommandPrompt= appendString "c:\> "
Теперь путем приправления простой функции concatStrings карри, можно легко добавить, что DOS разрабатывает командную строку к передней стороне любой строки! Действительно полезный!
Хорошо, не действительно. Более полезный случай, который я нахожу, - когда я хочу иметь делание функции, которая возвращает меня данные в потоке как способ.
let readDWORD array i = array[i] | array[i + 1] << 8 | array[i + 2] << 16 |
array[i + 3] << 24 //I've actually used this function in Python.
удобная часть об этом - то, что вместо того, чтобы создать весь класс для этого вида вещи, вызывая конструктора, звоня obj.readDWORD (), у Вас просто есть функция, которая не может быть видоизменена из-под Вас.
Вы знаете, что можно сопоставить функцию списку? Например, отображение функции для добавления единицы к каждому элементу списка:
> List.map ((+) 1) [1; 2; 3];;
val it : int list = [2; 3; 4]
Фактически это уже использует каррирование, потому что оператор (+)
использовался для создания функции для добавления единицы к своему аргументу, но вы можете выжать из этого примера немного больше, изменив его, чтобы отобразить ту же функцию списка списков:
> List.map (List.map ((+) 1)) [[1; 2]; [3]];;
val it : int list = [[2; 3]; [4]]
Без каррирования вы не смогли бы частично применить эти функции, и вместо этого пришлось бы написать что-то вроде этого:
> List.map((fun xs -> List.map((fun n -> n + 1), xs)), [[1; 2]; [3]]);;
val it : int list = [[2; 3]; [4]]