문제

나는이 코드를 가지고 있으며, 포인트없이 만들고 싶은 코드가 있습니다.

(\k t -> chr $ a + flip mod 26 (ord k + ord t -2*a))

어떻게해야하나요?

또한 "이 AMD에 대해 생각해 보는 것"이외의 포인트 프리 스타일에 대한 일반적인 규칙이 있습니까?

도움이 되었습니까?

해결책

함수를 돌립니다

func x y z = (some expression in x, y and z)

Point-Free Form으로, 나는 일반적으로 마지막 매개 변수로 수행 된 작업을 따르려고 노력합니다. z 그리고 기능을 다음과 같이 작성하십시오

func x y z = (some function pipeline built using x and y) z

그런 다음 취소 할 수 있습니다 z얻을 수 있습니다

func x y = (some function pipeline built using x and y)

그런 다음 y와 x에 대한 프로세스를 반복하면 끝납니다. func 포인트가없는 형태로. 이 과정에서 인식하는 필수 변환은 다음과 같습니다.

    f z = foo $ bar z    -- or f z = foo (bar z)
<=> f z = foo . bar $ z
<=> f   = foo . bar

부분 평가를 사용하면 기능에 대한 마지막 인수를 "해제"할 수 있음을 기억하는 것이 중요합니다.

foo $ bar x y == foo . bar x $ y    -- foo applied to ((bar x) applied to y)

특정 기능의 경우 그 흐름을 고려하십시오 k 그리고 t 통과:

  1. 적용하다 ord 그들 각각에게
  2. 결과를 추가하십시오
  3. 2*a
  4. 결과 모드 26을 가져 가십시오
  5. 을 추가하다
  6. 적용하다 chr

단순화에 대한 첫 번째 시도로서 우리는 다음을 얻습니다.

func k t = chr . (+a) . (`mod` 26) . subtract (2*a) $ ord k + ord t

피할 수 있습니다 flip 섹션을 사용하여 mod, 및 섹션 사용 - Haskell에서 지저분 해져서 subtract 함수 (음수를 쓰기 위해 구문과 충돌합니다. (-2) 음수 2를 의미하며 동일하지 않습니다 subtract 2).

이 기능에서 ord k + ord t 사용하기위한 훌륭한 후보입니다 Data.Function.on (링크). 이 유용한 조합을 사용하면 교체 할 수 있습니다 ord k + ord t 기능이 적용됩니다 k 그리고 t:

func k t = chr . (+a) . (`mod` 26) . subtract (2*a) $ ((+) `on` ord) k t

우리는 이제 매우 가깝습니다

func k t = (function pipeline) k t

따라서

func = (function pipeline)

불행히도 Haskell은 일련의 단수 기능으로 이진 함수를 작성할 때 약간 지저분하지만 트릭이 있습니다 (좋은 참조를 찾을 수 있는지 확인하겠습니다).

import Data.Function (on)

func = ((chr . (+a) . (`mod` 26) . subtract (2*a)) .) . ((+) `on` ord)

추악한 작곡 트릭을 제외하고는 거의 멋진 깔끔한 포인트가없는 기능 파이프 라인입니다. 정의함으로써 .: 연산자는 의견에서 제안했습니다 이 페이지에서,이 정리는 다음과 같습니다.

import Data.Function (on)

(.:) = (.).(.)

func = (chr . (+a) . (`mod` 26) . subtract (2*a)) .: ((+) `on` ord)

이것을 좀 더 연마하기 위해, 당신은 일부 도우미 기능을 추가하여 문자 <-> int 변환을 시저 암호 산수. 예를 들어: letterToInt = subtract a . ord

다른 팁

또한 "이 AMD에 대해 생각해 보는 것"이외의 포인트 프리 스타일에 대한 일반적인 규칙이 있습니까?

Lambdabot (Freenode의 #Haskell로 이동하거나 예를 들어 산에 GHCI). 코드의 경우 pl은 다음과 같습니다.

((chr . (a +) . flip mod 26) .) . flip flip (2 * a) . ((-) .) . (. ord) . (+) . ord

당신이 나에게 물어 보면 실제로 개선이 아닙니다.

표현을 포인트 프리 스타일로 변환하는 데는 일련의 트릭이 있습니다. 나는 전문가라고 주장하지는 않지만 여기에 몇 가지 팁이 있습니다.

먼저, 표현식의 가장 올바른 용어로 함수 인수를 분리하려고합니다. 여기의 주요 도구가있을 것입니다 flip 그리고 $, 규칙 사용 :

f a b ==> flip f b a
f (g a) ==> f $ g a

어디 f 그리고 g 기능입니다 a 그리고 b 표현입니다. 그래서 시작하려면 :

(\k t -> chr $ a + flip mod 26 (ord k + ord t -2*a))
-- replace parens with ($)
(\k t -> chr $ (a +) . flip mod 26 $ ord k + ord t - 2*a)
-- prefix and flip (-)
(\k t -> chr $ (a +) . flip mod 26 $ flip (-) (2*a) $ ord k + ord t)
-- prefix (+)
(\k t -> chr $ (a +) . flip mod 26 $ flip (-) (2*a) $ (+) (ord k) (ord t))

이제 우리는 가져 가야합니다 t 오른쪽에서. 이렇게하려면 규칙을 사용하십시오.

f (g a) ==> (f . g) a

그래서 :

-- pull the t out on the rhs
(\k t -> chr $ (a +) . flip mod 26 $ flip (-) (2*a) $ ((+) (ord k) . ord) t)
-- flip (.) (using a section)
(\k t -> chr $ (a +) . flip mod 26 $ flip (-) (2*a) $ ((. ord) $ (+) (ord k)) t)
-- pull the k out
(\k t -> chr $ (a +) . flip mod 26 $ flip (-) (2*a) $ ((. ord) . ((+) . ord)) k t)

이제 우리는 모든 것을 왼쪽으로 돌려야합니다. k 그리고 t 하나의 큰 함수 용어로, 우리가 양식의 표현을 갖도록 (\k t -> f k t). 이것은 상황이 약간의 마음을 굽히는 곳입니다. 우선, 마지막까지 모든 용어가 $ 단일 인수가있는 기능이므로 다음을 구성 할 수 있습니다.

(\k t -> chr . (a +) . flip mod 26 . flip (-) (2*a) $ ((. ord) . ((+) . ord)) k t)

이제 우리는 유형의 함수가 있습니다 Char -> Char -> Int 우리는 유형의 함수로 구성하고 싶어합니다. Int -> Char, 유형의 함수를 산출합니다 Char -> Char -> Char. (매우 이상한) 규칙을 사용하여 달성 할 수 있습니다.

f (g a b) ==> ((f .) . g) a b

그것은 우리에게 준다 :

(\k t -> (((chr . (a +) . flip mod 26 . flip (-) (2*a)) .) . ((. ord) . ((+) . ord))) k t)

이제 베타 감소 만 적용 할 수 있습니다.

((chr . (a +) . flip mod 26) .) . (flip flip (2*a) . ((-) . ) . ((. ord) . (+) .ord))

나는 당신의 포인트 프리 잉의 요점이 코드를 더 간결하고 읽기 쉽게 만드는 것이라고 가정합니다. 그러므로 나는 단순화를 위해 다른 리팩토링을 수행하는 것이 현명하다고 생각합니다. 그러면 변수를 쉽게 제거 할 수 있습니다.

(\k t -> chr $ a + flip mod 26 (ord k + ord t - 2*a))

우선, flip 불필요하다 :

(\k t -> chr $ a + (ord k + ord t - 2*a) `mod` 26)

다음으로 사용하겠습니다 이름과 정복 독립적으로 사용 가능한 하위 기능을 고려하기 위해 :

encode_characters k t = chr $ encode (ord k) (ord t)
encode x y = (x + y - 2*a) `mod` 26 + a

나는 또한 첫 표현에 이름을 밝히고 더 명확하고 재사용 할 수있게했습니다. encode_characters 이제 @nefrubyr의 기술을 사용하여 포인트 프리를 쉽게 만들 수 있습니다.

encode_characters = chr . encode `on` ord

두 번째 표현에 관해서는, 다른 답변에 표시된 것보다 더 읽을 수있는 양식을 생성 할 수 없으며 모두 포인트 별 형식보다 읽기 어려워집니다. 그러므로 나는이 시점에서 리팩토링을 중단하고 결과 코드의 청결과 재사용 성을 존경 할 것을 제안합니다.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

추신 : 운동으로서, 문제의 맥락에 따라, 기능 인터페이스의 약간의 수정 (함수로 전달되는 형태의 데이터)이 문제를 일반화하여 더 단순화 할 수 있습니다.

A. 기능을 구현하고 단순화합니다 encode_n_characters :: [Char] -> Char 어디 encode_characters k t = encode_n_characters [k, t]. 결과가 전문화 된 두 가지 기능보다 간단합니까?

B. 함수를 구현하십시오 encode' 정의 encode' (x + y) = encode x y 그리고 상환 encode_characters 이 기능을 사용합니다. 기능 중 하나가 더 단순 해지십니까? 구현이 전체적으로 더 간단합니까? ~이다 encode' 다소 재사용 할 수 있습니다 encode?

연결하십시오 IRC, #Haskell, 그리고 Lambdabot에게 물어보세요!:

<you> @pl (\k t -> chr $ a + flip mod 26 (ord k + ord t -2*a))
<lambdabot> [the answer]
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top