Created
December 15, 2017 12:21
-
-
Save elm4ward/e28666398794c86decf6c46a7972d171 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
let flag: (Bool) -> String = { $0 ? "β " : "β" } | |
// ------------------------ | |
// Predicative | |
// ------------------------ | |
protocol Predicative { | |
static func + (lhs: Self, rhs: Self) -> Self | |
static func * (lhs: Self, rhs: Self) -> Self | |
static func gt<P: PredicateLike>(_: P.Input) -> P where P.Input: Comparable, P.Output == Self | |
static func lt<P: PredicateLike>(_: P.Input) -> P where P.Input: Comparable, P.Output == Self | |
static func eq<P: PredicateLike>(_: P.Input) -> P where P.Input: Equatable, P.Output == Self | |
static func or<P: PredicateLike>(_: P, _: P) -> P where P.Output == Self | |
static func and<P: PredicateLike>(_: P, _: P) -> P where P.Output == Self | |
static func run<P: PredicateLike>(_: P) -> (P.Input) -> Self where P.Output == Self | |
} | |
extension Predicative { | |
static func or<P: PredicateLike>(_ p1: P, _ p2: P) -> P where P.Output == Self { | |
return .init(p1, p2, *) | |
} | |
static func and<P: PredicateLike>(_ p1: P, _ p2: P) -> P where P.Output == Self { | |
return .init(p1, p2, +) | |
} | |
static func run<P: PredicateLike>(_ p: P) -> (P.Input) -> Self where P.Output == Self { | |
return p.run | |
} | |
} | |
// ------------------------ | |
// Predicate | |
// ------------------------ | |
protocol PredicateLike { | |
associatedtype Input | |
associatedtype Output: Predicative | |
init(_: Input, _: @escaping (Input, Input) -> Output) | |
init(_: Self, _: Self, _: @escaping (Output, Output) -> Output) | |
func run(_ i: Input) -> Output | |
} | |
extension PredicateLike { | |
static func leaf<P>(_ i: P.Input, _ b: @escaping (P.Input, P.Input) -> P.Output) -> P where P: PredicateLike { | |
return .init(i, b) | |
} | |
static func pred<P>(_ p1: P, _ p2: P, _ f: @escaping (P.Output, P.Output) -> P.Output) -> P where P: PredicateLike { | |
return .init(p1, p2, f) | |
} | |
static func or<P: PredicateLike>(_ p1: P, _ p2: P) -> P { | |
return P.Output.or(p1, p2) | |
} | |
static func and<P: PredicateLike>(_ p1: P, _ p2: P) -> P { | |
return P.Output.and(p1, p2) | |
} | |
static func lt<P: PredicateLike>(_ i: P.Input) -> P where P.Input: Comparable { | |
return P.Output.lt(i) | |
} | |
static func gt<P: PredicateLike>(_ i: P.Input) -> P where P.Input: Comparable { | |
return P.Output.gt(i) | |
} | |
static func eq<P: PredicateLike>(_ i: P.Input) -> P where P.Input: Equatable { | |
return P.Output.eq(i) | |
} | |
} | |
func || <P>(lhs: P, rhs: P) -> P where P: PredicateLike { | |
return .or(lhs, rhs) | |
} | |
func && <P>(lhs: P, rhs: P) -> P where P: PredicateLike { | |
return .and(lhs, rhs) | |
} | |
func gt<P: PredicateLike>(_ j: P.Input) -> P where P.Input: Comparable { | |
return .gt(j) | |
} | |
func lt<P: PredicateLike>(_ j: P.Input) -> P where P.Input: Comparable { | |
return .lt(j) | |
} | |
func eq<P: PredicateLike>(_ j: P.Input) -> P where P.Input: Equatable { | |
return .eq(j) | |
} | |
// ------------------------ | |
// PRED | |
// ------------------------ | |
enum Predicate<I, O: Predicative>: PredicateLike { | |
typealias Input = I | |
typealias Output = O | |
case _reduceLeaf(I, (I, I) -> O) | |
indirect case _reducePred(Predicate, Predicate, (O, O) -> O) | |
init(_ i: Input, _ f: @escaping (Input, Input) -> Output) { | |
self = ._reduceLeaf(i, f) | |
} | |
init(_ p1: Predicate<I, O>, _ p2: Predicate<I, O>, _ f: @escaping (O, O) -> O) { | |
self = ._reducePred(p1, p2, f) | |
} | |
func run(_ i: I) -> O { | |
switch self { | |
case let ._reduceLeaf(j, reduce): | |
return reduce(i, j) | |
case let ._reducePred(p, q, reduce): | |
return reduce(p.run(i), q.run(i)) | |
} | |
} | |
} | |
// ------------------------ | |
// Bool | |
// ------------------------ | |
extension Bool: Predicative { | |
static func + (lhs: Bool, rhs: Bool) -> Bool { | |
return lhs && rhs | |
} | |
static func * (lhs: Bool, rhs: Bool) -> Bool { | |
return lhs || rhs | |
} | |
static func gt<P>(_ i: P.Input) -> P where P: PredicateLike, P.Input: Comparable, P.Output == Bool { | |
return .leaf(i, >) | |
} | |
static func lt<P>(_ i: P.Input) -> P where P: PredicateLike, P.Input: Comparable, P.Output == Bool { | |
return .leaf(i, <) | |
} | |
static func eq<P>(_ i: P.Input) -> P where P: PredicateLike, P.Input: Equatable, P.Output == Bool { | |
return .leaf(i, ==) | |
} | |
} | |
// ------------------------ | |
// String | |
// ------------------------ | |
extension String: Predicative { | |
static func * (lhs: String, rhs: String) -> String { | |
return lhs + " || " + rhs | |
} | |
static func gt<P>(_ i: P.Input) -> P where P: PredicateLike, P.Input: Comparable, P.Output == String { | |
return .leaf(i, { "\($0) > \($1)" }) | |
} | |
static func lt<P>(_ i: P.Input) -> P where P: PredicateLike, P.Input: Comparable, P.Output == String { | |
return .leaf(i, { "\($0) < \($1)" }) | |
} | |
static func eq<P>(_ i: P.Input) -> P where P: PredicateLike, P.Input: Equatable, P.Output == String { | |
return .leaf(i, { "\($0) == \($1)" }) | |
} | |
} | |
// ------------------------ | |
// N | |
// ------------------------ | |
struct N: Predicative { | |
let s: String | |
let b: Bool | |
static func + (lhs: N, rhs: N) -> N { | |
return N(s: lhs.s + rhs.s, b: lhs.b + rhs.b) | |
} | |
static func * (lhs: N, rhs: N) -> N { | |
return N(s: lhs.s * rhs.s, b: lhs.b * rhs.b) | |
} | |
static func gt<P>(_ i: P.Input) -> P where P: PredicateLike, P.Input: Comparable, P.Output == N { | |
return .leaf(i, { N(s:"(\($0) > \($1) [\(flag($0 > $1))])", b: $0 > $1) }) | |
} | |
static func lt<P>(_ i: P.Input) -> P where P: PredicateLike, P.Input: Comparable, P.Output == N { | |
return .leaf(i, { N(s:"(\($0) < \($1) [\(flag($0 < $1))])", b: $0 < $1) }) | |
} | |
static func eq<P>(_ i: P.Input) -> P where P: PredicateLike, P.Input: Equatable, P.Output == N { | |
return .leaf(i, { N(s:"(\($0) == \($1) [\(flag($0 == $1))])", b: $0 == $1) }) | |
} | |
} | |
// ------------------------ | |
// Sequence | |
// ------------------------ | |
extension Sequence { | |
func filter(by: IPredicate<Element, Bool>) -> [Element] { | |
return filter(by.run) | |
} | |
func filter(by: IPredicate<Element, N>) -> String { | |
return map { e in | |
let n = by.run(e) | |
return "\(flag(n.b)) \(e): \n " + n.s | |
}.joined(separator: "\n ") | |
} | |
} | |
// ------------------------ | |
// IPredicate | |
// ------------------------ | |
struct IPredicate<I, O: Predicative> { | |
let run: (I) -> O | |
init(_ f: @escaping (I) -> O) { | |
self.run = f | |
} | |
init<P>(_ target: KeyPath<I, P>, _ predicate: Predicate<P, O>) { | |
run = { i in O.run(predicate)(i[keyPath: target]) } | |
} | |
func liftD(_ ip: IPredicate<I, O>, _ f: @escaping (O, O) -> O) -> IPredicate<I, O> { | |
return IPredicate { i in f(self.run(i), ip.run(i)) } | |
} | |
} | |
func == <R, I, P>(lhs: KeyPath<R, I>, rhs: Predicate<I, P>) -> IPredicate<R, P> where I: Comparable { | |
return IPredicate(lhs, rhs) | |
} | |
func && <I, O>(lhs: IPredicate<I, O>, rhs: IPredicate<I, O>) -> IPredicate<I, O> { | |
return lhs.liftD(rhs, +) | |
} | |
func || <I, O>(lhs: IPredicate<I, O>, rhs: IPredicate<I, O>) -> IPredicate<I, O> { | |
return lhs.liftD(rhs, *) | |
} | |
//// ------------------------ | |
//// Example | |
//// ------------------------ | |
struct P: CustomStringConvertible { | |
let name: String | |
let age: Int | |
let address: A | |
var description: String { | |
return "P(name: \(name), age: \(age))" | |
} | |
} | |
struct A { | |
let country: String | |
} | |
let allPersons = [ | |
P(name: "a", age: 40, address: A(country: "π¨π¦")), | |
P(name: "a", age: 31, address: A(country: "π¬π§")), | |
P(name: "b", age: 21, address: A(country: "π©πͺ")), | |
P(name: "b", age: 50, address: A(country: "π«π·")), | |
P(name: "c", age: 60, address: A(country: "π―π΅")) | |
] | |
let result1: [P] = allPersons.filter(by: | |
\.age == eq(40) || \.name == eq("a") | |
) | |
let result2: String = allPersons.filter(by: | |
\.age == eq(40) || \.name == eq("a") | |
) | |
print(result2) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment