2017-10-10 8 views
1

내가 인수가 카레 구성 기능에 퍼져되어야 할 때 pointfree 구성 기능을 쓰기위한 패턴이 있는지 파악하기 위해 노력하고있어
즉 (Ramda 포함) :Pointfree 구성 기능

add_1_and_multiply = (add, mul) => R.compose(R.multiply(mul), R.add(1))(add) 
add_1_and_multiply(3, 5) // 20 

add_1_and_multiply 쓰는 방법 pointfree 스타일로?

+0

시도가 ((1, x)를 y를 추가) 다중 '에 대한 구성을 사용하는'대신'곱셈의 (Y ((1)를 추가, 배))''R. compose (R.multiply, R.add (1))' – Bergi

+0

@Bergi 정확히 내 대답을 결론 지었다. –

답변

2

pointfree 스타일과 non-unary arity를 ​​쉽게 결합 할 수 있는지 확실하지 않습니다. 결과 및 구성 기능의 유형을해야 무엇을 먼저 생각 :

// Compose:   (B -> C) -> (A -> B) -> A -> C 
const compose = f => g => x => f(g(x)) 
// Add:    A -> A -> A 
const add = x => y => x + y 
// Mul:    A -> A -> A 
const mul = x => y => x * y 

// Add1:    A -> A 
const add1 = add(1) 

// Add1AndMul:  A ->   A -> A 
// because: 
//  Add1:   A -> A 
//  Mul:     A -> A -> A 
const add_1_and_mul = compose(mul)(add1) 

// Mul4:    A -> A 
const mul_4 = add_1_and_mul(3) 
const result = mul_4(5) //> 20 

Ramda이 uncurryN 그래서 당신이 compose 주위를 포장 할 수 있으며 결과 함수의 태닝을 제거있다.


const add_1_and_multiply = R.uncurryN(2, R.compose(R.multiply, R.add(1))) 
let result2 = add_1_and_multiply(3, 5) //> 20 

는 이전 기능을 구성하는 데 필요한 "체인"에 다른 기능을 추가합니다.

// Add1AndMul:   A -> A -> A 
const add1_mul = compose(mul)(add1) 

이것은 우리가 원하는 서명입니다.

//      1   2   3 
// Add1AndMulAndAdd: A ->  A ->  A -> A 
// which is:   |   |   | 
//  Add1:   A -> A |   | 
//  Mul:     A -> A -> A | 
//  Add:       A -> A -> A 

그래서 어떻게 든 우리는 그 어떤 "포인트"가없는 과 을 통과해야합니다. 그냥 간단한 구성을 시도하고 그것을 분석하자 : 작성의

let add1_mul_add = compose(add)(add1_mul) 

Remeber 서명 : (E -> F) -> (D -> E) -> D -> F를! 단계에서 분석 :

  1. 우리는 대신 (E -> F)

    (E -> F ) 
    (A -> A -> A) 
    

    의 우리의 add 함수 서명을 공급 우리는

    E = A 
    F = A -> A 
    
  2. 우리는 (D -> E)add1_mul

    에 동일한 작업을 수행 결론(210)
    (D -> E ) 
    (A -> A -> A) 
    

    우리는

    D = A 
    E = A -> A 
    

그러나 우리가 이미 모순을 볼 수 있다는 결론! 2 단계에서 결론은 1 단계에서 결론을 모순됩니다. EAA -> A 일 수 없습니다.

따라서 addadd1_mul을 작성할 수없고 add1_mul_add은 오류를 발생시킵니다.

문제를 해결하고 pointfree 스타일에 대한 약속을 어기도록 수정합시다.

const add1_mul_add = x => compose(add)(add1_mul(x)) 

나는 나의 점을 설명하기 위해 몇 가지 규칙을 깰 및 코드 서명을 혼합거야 :
x -> (A -> A -> A) -> (x -> A -> A) -> A -> A -> A 
         || 
         \/ 
x -> (A -> A -> A) -> (A -> A) -> A -> A -> A 
    (E -> F ) -> (D -> E) -> D -> F 

그래서 우리는 우리의 올바른 작성 서명을 얻었다! x 변수를 제거하여 pointfree로 되돌리려면 어떻게해야합니까? 우리는 예를 들어 ... 우리 예 오래 된 기능 구성과 같은 명백한 패턴을 찾아 볼 수 있습니다!

f(g(x)) => compose(f)(g) 

그리고 우리는 우리의 새로운 add1_mul_add에서이 패턴을 찾을 수 -

f = compose(add) 
g = add1_mul 
f(g(x)) = compose(add)(add1_mul(x)) 

을 우리가 pointfree 그것을 줄이고 우리가 우리의 새로운 add1_mul_add 기능을 가지고 :

const add1_mul_add = compose(compose(add))(add1_mul) 

을하지만, 이봐 - 우리가 할 수있는 더 줄여라!

const add1_mul_add = compose(compose)(compose)(add)(add1_mul) 

그리고 거기 우리는 이미 The Owl의 이름으로 하스켈에 존재하는 것을 발견했다. 체인의 모든 새로운 기능, 당신은 올빼미 연산자의 높은 순서를 만들어야합니다, 지금

const owl = compose(compose)(compose) 

그러나 :

우리는 단순히 자바 스크립트를 정의 할 수 있습니다.

const owl2 = compose(compose)(owl) 
const add1_mul_add_mul = owl2(mul)(add1_mul_add) 

const owl3 = compose(compose)(owl2) 
const add1_mul_add_mul_add = owl3(add)(add1_mul_add_mul) 

따라서 함수를 pointfree 스타일로 단항하는 것이 좋습니다. 또는 목록과 같은 다른 구조를 사용

const actions = [ add, mul, add, mul ] 
const values = [ 1, 2, 3, 4 ] 
const add_mul_add_mul = (...values) => zip(actions, values).reduce((acc, [action, value]) => action(acc, value), 0) 
+1

wow 그냥 작동 ..하지만 같은 기능을 중첩해야합니다 추가 기능을 작성하는 데 필요한 경우 작성하는 2 함수 함께 작동합니다.'add_1_and_multiply_and_add = R.uncurryN (2, R.compose (R.add, R (3, 5, 6) //> 26'을 사용하면'spread_compose'를 구현하는 것이 가능해 보입니다. 함수의 수에 관계없이 함수를 수행하는 함수 – aleclofabbro

+0

이론상'compose'는 2 진 함수이기 때문에 2 개의 함수에서만 작동합니다. 따라서 "체인"에 다른 함수를 추가하려면 이미 작성된 함수로 함수를 작성해야합니다. 나는 대답을 업데이트 할 것이다. –

+0

답변을 업데이트했습니다. –