26

今日のハッカーニュースでこの投稿を見ました。私は、純粋な関数型プログラミングが現実世界の問題を抽象化するのにどのように役立つかを理解するという同じ問題に苦しんでいます。私は 7 年前に命令型プログラミングから OO プログラミングに切り替えました。私はそれをマスターしたと感じています。ここ数年で、map や reduce などの関数型プログラミングのトリックや概念をいくつか学びましたが、それらも気に入っています。私はオブジェクト指向コードでそれらを使用しており、それに満足していますが、一連の命令を抽象化するとき、コードをより美しくするためのオブジェクト指向抽象化しか考えられません。

最近、私は python の問題に取り組んでおり、それを解決するために OO を使用しないようにしています。ほとんどの場合、私のソリューションは必須のように見えますが、OO を使用すれば見栄えが良く、きれいに見えることはわかっています。私は問題を投稿しようと思っていました.機能の専門家が美しく機能的な解決策を提示できるかもしれません. 必要に応じて醜いコードを投稿できますが、投稿したくありません。:) 問題は次のとおりです。

ユーザーは画像または画像のサムネイルをリクエストできます。ユーザーが画像のサムネイルを要求し、それがまだ存在しない場合は、python の PIL モジュールを使用して作成します。また、元の画像名はハッシュコードであり、その内容を説明するものではないため、人が読めるパスを使用して元の画像またはサムネイルへのシンボリック リンクを作成します。最後に、そのイメージのシンボリック リンクにリダイレクトします。

OO では、SymlinkImage 基本クラス、ThumbnailSymlinkImage サブクラス、および OriginalSymlinkImage サブクラスを作成する可能性があります。共有データ(SymlinkImage クラス内)は、オリジナルへのパスのようなものになります。共有動作は、シンボリック リンクを作成します。サブクラスは、該当する場合はサムネイルを作成し、スーパークラスを呼び出して新しいシンボリック リンクを作成する「generate」などと呼ばれるメソッドを実装します。

4

3 に答える 3

20

ええ、実際には関数型アプローチを使用してこれを非常に異なる方法で行うでしょう。

これは、型付きで、デフォルトで純粋な関数型プログラミング言語Haskellを使用したスケッチです。問題の主要な概念に対して新しいタイプを作成し、作業を一度に 1 つのタスクを実行する個別の関数に分割します。IO およびその他の副作用 (シンボリック リンクの作成など) は、特定の機能に制限され、型で示されます。2 つの操作モードを区別するために、合計タイプを使用します。

--
-- User can request an image or a thumbnail of the image.
-- If the user requests the thumbnail of the image, and it doesn't yet exist, create it using
-- python's PIL module. Also create a symbolic link to the original or
-- thumbnail with a human readable path, because the original image name is a
-- hashcode, and not descriptive of it's contents. Finally, redirect to the
-- symbolic link of that image.
--

module ImageEvent where

import System.FilePath
import System.Posix.Files

-- Request types
data ImgRequest = Thumb ImgName | Full ImgName

-- Hash of image 
type ImgName = String

-- Type of redirects
data Redirect

request :: ImgRequest -> IO Redirect
request (Thumb img) = do
    f <- createThumbnail img
    let f' = normalizePath f
    createSymbolicLink f f'
    return (urlOf f)

request (Full img)  = do
    createSymbolicLink f f'
    return (urlOf f)
    where
        f  = lookupPath img
        f' = normalizePath f

いくつかのヘルパーとともに、定義はあなたに任せます。

-- Creates a thumbnail for a given image at a path, returns new filepath
createThumbnail :: ImgName -> IO FilePath
createThumbnail f = undefined
    where
        p = lookupPath f

-- Create absolute path from image hash
lookupPath :: ImgName -> FilePath
lookupPath f = "/path/to/img" </> f <.> "png"

-- Given an image, construct a redirect to that image url
urlOf :: FilePath -> Redirect
urlOf = undefined

-- Compute human-readable path from has
normalizePath :: FilePath -> FilePath
normalizePath = undefined

真に美しいソリューションは、要求/応答モデルをデータ構造で抽象化して、実行する一連のコマンドを表すことです。リクエストが来て、それが何をする必要があるかを純粋に表現するための構造を構築し、それが実行エンジンに渡され、ファイルの作成などを行います。その場合、コア ロジックは完全に純粋な関数になります (この問題には多くのコア ロジックが含まれているわけではありません)。エフェクトを使用した真に純粋な関数型プログラミングのこのスタイルの例として、Wouter Swiestra の論文「Beauty in the Beast: A Functional Semantics for the Awkward Squad」をお勧めします。

于 2011-05-31T17:07:42.807 に答える
5

個人的には、関数型プログラミングを使用して、命令型プログラミング用に設計/記述されている問題を解決しようとしていることが問題だと思います。よく使われる 3 つのパラダイム (関数型、命令型、オブジェクト指向) にはそれぞれ異なる長所があります。

  • 関数型プログラミングは、通常、入力/結果の観点から、何を行うべきかの記述に重点を置いています。
  • 命令型プログラミングは、通常、実行するステップのリストと順序、および変更する状態の観点から、何かを行う方法を強調します。
  • オブジェクト指向プログラミングは、システム内のエンティティ間の関係を重視します

したがって、問題に取り組むときの最初の仕事は、意図したパラダイムで適切に解決できるように言い換えることです。ちなみに、サイドノードとして「純粋なOOP」というものは私の知る限りありません。OOP クラス (Java、C#、C++、Python、Objective C など) のメソッド内のコードはすべて命令型です。

例に戻ると、問題を述べる方法 (最初に、次に、最後に) は本質的に必須です。そのため、機能的な解決策を構築することはほとんど不可能です (つまり、副作用やモナドなどのトリックを行わなければ)。同様に、たくさんのクラスを作成したとしても、それらのクラスはそれ自体では役に立ちません。それらを使用するには、問題を段階的に解決する命令型コード (これらのコードはクラス内に埋め込まれていますが) を作成する必要があります。

問題を言い換えるには:

  • 入力: 画像タイプ (フルまたはサムネイル)、画像名、ファイル システム
  • 出力: 要求されたイメージ、要求されたイメージを含むファイル システム

新しい問題ステートメントから、次のように解決できます。

def requestImage(type, name, fs) : 
    if type == "full" :
        return lookupImage(name, fs), fs
    else:
        thumb = lookupThumb(name, fs)
        if(thumb) :
            return thumb, fs
        else:
            thumb = createThumbnail(lookupImage(name, fs))
            return thumb, addThumbnailToFs(fs, name, thumb)

もちろん、これは不完全ですが、lookupImage、lookupThumb、createThumbnail、および addThumbnailToFs はほぼ同じ方法でいつでも再帰的に解決できます。

大きな注意: 新しいファイルシステムを作成するのは大変に思えますが、そうすべきではありません。たとえば、これがより大きな Web サーバーのモジュールである場合、「新しいファイルシステム」は、新しいサムネイルを配置する場所への指示と同じくらい簡単です。または、最悪の場合、サムネイルを適切な場所に配置するための IO モナドになる可能性があります。

于 2011-05-31T17:37:14.667 に答える
5

考え方を変える唯一の方法は、考え方を変えることです。私にとって何がうまくいったかを教えてください:

並行性が必要な個人的なプロジェクトに取り組みたかったのです。私は周りを見回して、erlangを見つけました。それを選んだ理由は、他の理由ではなく、同時実行性をサポートするのに最適だと思ったからです。私はそれまで関数型言語を扱ったことがありませんでした (比較のために、1990 年代初頭にオブジェクト指向プログラミングを始めました)。

アームストロングのアーランの本を読みました。大変でした。取り組むべき小さなプロジェクトがありましたが、それを叩き続けました。

このプロジェクトは失敗に終わりましたが、数か月後、頭の中ですべてを十分にマッピングしたので、以前のようにオブジェクトについて考えることができなくなりました。

オブジェクトを erlang プロセスにマッピングするフェーズを経ましたが、それほど長くはなかったので、そこから抜け出しました。

パラダイムを切り替えることは、言語を切り替えることや、ある車から別の車に乗り換えるようなものです。父の車を運転するのは、私のトラックとは違う感じですが、再び慣れるのにそれほど時間はかかりません.

Python での作業が足を引っ張っている可能性があると思います。erlang をチェックすることを強くお勧めします。その構文は非常に異質ですが、それも良いことです。より伝統的な構文は (少なくとも私にとっては) 古い方法でプログラムしようとしてイライラすることにつながるからです。

于 2011-05-31T17:10:48.173 に答える