2 posts tagged

functional

Композиция функций

В функциональном программировании часто используется построение функций, например построение функции f3 из двух функций f1 и f2 применив к ним функцию F, это и называется композицией функций. Расмотрим на простом примере. Создадим несколько функций, которые будут передаваться как параметры последовательно, первая функция создает и возвращает массив с работниками:

func createTeam(count: Int) -> [Employee] {
    var team = [Employee]()
    for i in 1...count {
        let f = i % 2 == 0
        team.append(Employee(id: i, pay: f ? i * 200 : i * 100, type: f ? .Dev : .QA))
    }
    return team
}

Вторая функция выбирает из масива только разработчиков и возвращает новый массив:

func getDevs(employees: [Employee]) -> [Employee] {
    return employees.filter({$0.type == .Dev})
}

И третья функция цепочки возвращает сумарную зарплату работников:

func getAverageSalary(employees: [Employee]) -> Int {
    return employees.reduce(0){$0 + $1.pay}
}

Осталось написать последнюю функцию, которая и будет композицией:

let salary = {count in getAverageSalary(getDevs(createTeam(count)))}

Теперь можно вызвать salary передав ей параметр count:

salary(5)

Также можно создать свой оператор для композиции, например >>> с левой асоциативностью (об кастомных операторах):

infix operator >>> { associativity left }
func >>> <A, B, C>(f: B -> C, g: A -> B) -> A -> C {
    return { x in f(g(x)) }
}

В итоге конструкция получится более изящной:

let salary2 = getAverageSalary >>> getDevs >>> createTeam
salary2(5)
2016   Composition   functional   Swift

Карринг на примере сортировки

Карринг — прием в функциональном программировании, который позволяет преобразовать функцию с несколькими агрументами в последовательность новых функций с меньшим количеством аргументов. Карринг полезно применять в тех случаях, когда некоторые параметры функции известны зарание и передавать каждый раз одинаковые значение не имеет смысла. Например у нас есть функция для сортировки списка, у нее есть два параметра: тип сортировки и сам список, в случае, когда функция будет применяться для какого нибудь конкретного типа, например списка чатов, то передавать каждый раз сам список не нужно, так как у нас будет меняьтся только способ сортировки, или наоборот, сортировка будет всегда одинаковая, а исходные данные будут меняться.

Для примера возьмем массив элементов типа ChatEntity, у этого типа есть поле members, будем сортировать массив в зависимости от количества элементов в members. Напишем протокол и реализуем сортировку:

protocol SortChatProtocol {
    func sortList(members: [ChatEntity]) -> [ChatEntity]
}

class Members: SortChatProtocol {
    func sortList(members: [ChatEntity]) -> [ChatEntity] {
        return members.sort({
            return $0.members!.count > $1.members!.count
        })
    }
}

Теперь напишем каррированную функцию, которая сортирует список в зависимости от выбраной реализации, функция принимает обьект типа SortChatProtocol и возвращает функцию, которая в свою очередь принимает список с данными и возвращает отсортированый список:

func chatListSorterBy(order: SortChatProtocol)(chatList: [ChatEntity]) -> [ChatEntity] {
    return order.sortList(chatList)
}

К сожалению, на мой взгляд, в Swift 2.2 испортили синтаксис обьявления каррированных функций, теперь та же функция будет выглядеть так:

func chatListSorterBy(order: SortChatProtocol) -> ([ChatEntity]) -> [ChatEntity] {
    return { (chatList: [ChatEntity]) -> ([ChatEntity]) in
        return order.sortList(chatList)
    }
}

Осталось написать функцию, которая будет вызывать chatListSorterBy с типом сортировки и возвращать функцию с параметром [ChatEntity]:

func createChatListSorterBy(order: SortChatProtocol) -> [ChatEntity] -> [ChatEntity] {
    return chatListSorterBy(order)
}

В результате у нас получится функция sorter, которая принимает только список, так как мы уже зарание указали какую сортровку использовать.

let sorter = createChatListSorterBy(Members())
let sortedList = sorter(notSortedList)
let otherSortedList = sorter(otherNotSortedList)
2016   Currying   functional   programming   Swift