F# приправленная карри функция

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:, и вот эксперимент, который показывает, что:

1. Override 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

Установка контрольных точек или добавление вывода отладки показывает, что переопределенный оператор == никогда не вызывается. Я не знаю, является ли это ошибкой или предполагаемым поведением.

2. Override 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>

19
задан Roddy 2 October 2008 в 15:52
поделиться

4 ответа

(Редактирование: маленькое 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.

21
ответ дан 30 November 2019 в 02:30
поделиться

В то время как предыдущие примеры ответили на вопрос, вот два более простых примера того, как Приправление карри может быть выгодным для программирования 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];;
15
ответ дан 30 November 2019 в 02:30
поделиться

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

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 (), у Вас просто есть функция, которая не может быть видоизменена из-под Вас.

3
ответ дан 30 November 2019 в 02:30
поделиться

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

> 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]]
3
ответ дан 30 November 2019 в 02:30
поделиться
Другие вопросы по тегам:

Похожие вопросы: