努力使用纯函数编程来解决日常问题

问题描述

| 我今天在黑客新闻中看到了这篇文章。我在理解纯函数式编程将如何帮助我抽象一个现实问题方面遇到同样的问题。 7年前,我从命令式编程转换为面向对象编程。我觉得我已经掌握了它,并且对我很有帮助。在过去的几年中,我学到了一些函数编程的技巧和概念,例如map和reduce,我也喜欢它们。我在OO代码中使用了它们,对此感到满意,但是在抽象一组指令时,我只能想到OO抽象来使代码更漂亮。 最近,我一直在研究python中的问题,并且一直在尝试避免使用OO来解决它。在大多数情况下,我的解决方案看起来势在必行,而且我知道如果使用OO,可以使它看起来更干净。我以为我可以发布问题,也许功能专家可以提出一个既美观又实用的解决方案。如果必须的话,我可以发布我的丑陋代码,但宁愿不发布。 :)这是问题所在: 用户可以请求图像或图像的缩略图。如果用户要求图片的缩略图,但该缩略图尚不存在,请使用python的PIL模块创建该缩略图。另外,还应创建一条具有可读路径的原始链接或缩略图的符号链接,因为原始图像名称是哈希码,而不是其内容的描述性内容。最后,重定向到该图像的符号链接。 在OO中,我可能会创建一个SymlinkImage基类,ThumbnailSymlinkImage子类和OriginalSymlinkImage子类。共享数据(在SymlinkImage类中)将类似于原始数据的路径。共享行为将创建符号链接。子类将实现一个名为\'generate \'之类的方法,该方法将负责创建缩略图(如果适用),并调用其超类以创建新的符号链接。     

解决方法

        是的,您实际上会使用功能性方法来进行此操作。 这是使用默认的纯函数式编程语言Haskell的草图。我们针对您的问题的关键概念创建新的类型,并将工作分解为离散的函数,这些函数一次执行一项任务。 IO和其他副作用(如创建符号链接)仅限于某些功能,并以类型指示。为了区分两种操作模式,我们使用求和类型。
--
-- 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的论文《野兽之美:尴尬小队的功能语义》     ,我个人认为问题是您正在尝试使用函数式编程来解决为命令式编程设计/陈述的问题。三种流行的范例(功能,命令,面向对象)具有不同的优势: 函数式编程通常以输入/结果的形式强调要执行的操作。 命令式编程强调如何做某事,通常是按照要采取的步骤的清单和顺序,以及要修改的状态。 面向对象编程强调系统中实体之间的关系 因此,当您处理问题时,首要任务是重新表述该问题,以便预期的范例可以正确解决该问题。顺便说一下,据我所知,作为副节点,没有“纯OOP”这样的东西。 OOP类的方法(Java,C#,C ++,Python或Objective C)中的代码都是必须的。 回到您的示例:陈述问题的方式(首先,然后也是,最后)是本质上必须的。因此,构建功能性解决方案几乎是不可能的(即不进行副作用或monad之类的技巧)。同样,即使您创建了一堆类,这些类本身也是无用的。要使用它们,您必须编写命令式代码(尽管这些代码已嵌入类中)才能逐步解决问题。 重述该问题: 输入:图像类型(完整或缩略图),图像名称,文件系统 输出:请求的图像,带有请求图像的文件系统 从新的问题陈述中,您可以像这样解决它:
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,thumb)
当然,这是不完整的,但是我们总是可以以大致相同的方式递归地解决lookupImage,lookupThumb,createThumbnail和addThumbnailToF。 注意:创建新文件系统听起来很麻烦,但事实并非如此。例如,如果这是较大的Web服务器中的模块,则“新文件系统”可以与新缩略图应在何处的指令一样简单。或者,在最坏的情况下,将缩略图放置到适当的位置可能是IO monad。     ,        改变思维方式的唯一方法就是改变思维方式。我可以告诉您什么对我有用: 我想从事需要并发的个人项目。我环顾四周,发现了erlang。我之所以选择它,是因为我认为它为并发提供了最好的支持,而不是出于其他任何原因。我以前从未使用过函数式语言(为了比较,我从1990年代初开始进行面向对象的编程。) 我读了阿姆斯特朗的二郎本。辛苦了我有一个小项目要进行,但是我一直在努力。 该项目失败了,但是几个月后,我已经将所有内容都绘制在脑海中,以至于我不再像以前那样思考对象。 我确实经历了将对象映射到erlang进程的阶段,但这还不算太长,我就摆脱了。 现在,切换范例就像切换语言,或从一辆车到另一辆车。驾驶父亲的汽车与卡车的感觉不一样,但是很快就可以习惯了。 我认为使用Python工作可能会使您退缩,因此强烈建议您检查erlang。它的语法非常陌生-但这也很好,因为较传统的语法(至少对我而言)会导致尝试以旧的方式对其进行编程而感到沮丧。     

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...