17

ポイントフリーにしたいこのコードがあります。

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

それ、どうやったら出来るの?

また、「このamdについて考えて何かを思い付く」以外に、ポイントフリースタイルの一般的なルールはありますか?

4

5 に答える 5

52

機能を切り替えるには

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

ポイントフリー形式にするには、通常、最後のパラメーターに対して行われることに従いz、関数を次のように記述します。

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

z次に、 sをキャンセルして取得できます

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. 結果を mod 26 にします
  5. を追加
  6. 申し込みchr

したがって、単純化の最初の試みとして、次のようになります。

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

flipのセクションを使用することで回避できることに注意してください。また、Haskell では get messymodを使用するセクションを使用することで関数が存在することに注意してください (これらは負の数を記述するための構文と衝突します:負の 2 を意味し、 と同じではありません)。-subtract(-2)subtract 2

この関数では、 (リンクord k + ord t)を使用する優れた候補です。この便利なコンビネータを使用すると、 andに適用される関数に置き換えることができます。Data.Function.onord k + ord tkt

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

私たちは今、実現に非常に近づいています

func k t = (function pipeline) k t

それゆえ

func = (function pipeline)

残念ながら、一連の単項関数を使用して 2 項関数を構成する場合、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 変換をCaesar 暗号演算から分離することができます。例えば:letterToInt = subtract a . ord

于 2010-03-17T19:00:02.523 に答える
10

Also are there some general rules for point free style other than "think about this amd come up with something"?

You can always cheat and use the "pl" tool from lambdabot (either by going to #haskell on freenode or by using e.g. ghci on acid). For your code pl gives:

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

Which isn't really an improvement if you ask me.

于 2010-03-17T17:33:14.923 に答える
3

ポイントフリーのポイントは、コードをより簡潔で読みやすくすることだと思います。したがって、単純化に向けて他のリファクタリングを行うことも賢明だと思います。これにより、変数を簡単に削除できるようになります。

(\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

2番目の式については、他の回答に示されているフォームよりも読みやすいフォームを作成することはできず、それらはすべてポイントワイズフォームよりも読みにくくなっています。したがって、この時点でリファクタリングを停止し、結果として得られるコードのクリーンさと再利用性を賞賛することをお勧めします。

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

PS: 演習として、問題のコンテキストに応じて、関数インターフェイス (関数に渡される形式のデータ) を少し変更すると、問題を一般化することでさらに単純化できる場合があります。

A. 関数encode_n_characters :: [Char] -> Charwhereを実装して単純化しますencode_characters k t = encode_n_characters [k, t]。結果は特殊な 2 引数関数よりも単純ですか?

B.encode'経由で定義された関数encode' (x + y) = encode x yを実装encode_charactersし、この関数を使用して再実装します。どちらかの機能がシンプルになりますか? 実装は全体的に簡単ですか?encode'より多かれ少なかれ再利用可能ですencodeか?

于 2012-04-10T16:55:52.730 に答える
3

式をポイントフリー スタイルに変換するための一連のトリックが必ずあります。私は専門家ではありませんが、ここにいくつかのヒントがあります。

まず、関数の引数を式の一番右の項に分離します。ここでの主なツールは、ルールを使用してflipとになります。$

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

fgは関数であり、とab式です。開始するには:

(\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)

ここで、 の左側のすべてを 1 つの大きな関数項に変換する必要がありますktこれにより、 の形式の式が得られ(\k t -> f k t)ます。これは、物事が少し気が狂うところです。まず、最後までのすべての用語$は単一の引数を持つ関数であるため、それらを構成できることに注意してください。

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

ここで、 typeChar -> Char -> Intの関数で構成したい type の関数があり、 typeInt -> 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))
于 2010-03-17T20:29:03.390 に答える
0

IRC、 #haskellに接続し、 lambdabotに質問してください! :

<you> @pl (\k t -> chr $ a + flip mod 26 (ord k + ord t -2*a))
<lambdabot> [the answer]
于 2010-03-17T17:31:16.590 に答える