Haskell에서 점수가 없습니다
-
20-09-2019 - |
문제
나는이 코드를 가지고 있으며, 포인트없이 만들고 싶은 코드가 있습니다.
(\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
통과:
- 적용하다
ord
그들 각각에게 - 결과를 추가하십시오
- 2*a
- 결과 모드 26을 가져 가십시오
- 을 추가하다
- 적용하다
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]