我今天在hacker news看到了this post。我正在努力解决同样的问题,即理解纯函数式编程如何帮助我抽象现实世界的问题。7年前,我从命令式编程转向面向对象编程。我觉得我已经掌握了它,它对我很有帮助。在过去的几年里,我学到了函数式编程中的一些技巧和概念,比如map和reduce,我也很喜欢它们。我已经在我的OO代码中使用了它们,并且对此很满意,但在抽象一组指令时,我只能考虑OO抽象来使代码更美观。
最近我一直在研究python中的一个问题,我一直在努力避免使用OO来解决它。在很大程度上,我的解决方案看起来势在必行,我知道如果我使用OO,我可以让它看起来漂亮而干净。我想我应该发布这个问题,也许功能专家可以提出一个漂亮而实用的解决方案。如果有必要,我可以发布我丑陋的代码,但我不希望这样做。:)问题是这样的:
用户可以请求图像或图像的缩略图。如果用户请求图像的缩略图,但它还不存在,那么使用python的PIL模块创建它。还要创建一个指向原始图像或缩略图的符号链接,并使用人类可读的路径,因为原始图像的名称是一个哈希码,而不是对其内容的描述。最后,重定向到该图像的符号链接。
在OO中,我可能会创建一个SymlinkImage基类、一个ThumbnailSymlinkImage子类和一个OriginalSymlinkImage子类。共享数据(在SymlinkImage类中)将类似于原始数据的路径。共享行为将创建符号链接。子类将实现一个类似于'generate‘的方法,它将负责创建缩略图(如果适用),并调用它们的超类来创建新的符号链接。
发布于 2011-06-01 01:07:43
是的,使用函数式方法做这件事会有很大不同。
这是一个使用类型化的、默认的纯函数式编程语言Haskell的草图。我们为您的问题的关键概念创建新的类型,并将工作分解为一次完成一项任务的离散功能。IO和其他副作用(如创建符号链接)仅限于某些函数,并使用类型表示。为了区分这两种操作模式,我们使用a sum type。
--
-- 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-06-01 01:10:48
改变你的思维方式的唯一方法就是改变你的思维方式。我可以告诉你什么对我有效:
我想做一个需要并发的个人项目。我环顾四周,发现了erlang。我之所以选择它,是因为我认为它对并发的支持是最好的,而不是其他任何原因。我以前从未使用过函数式语言(为了进行比较,我在20世纪90年代初开始从事面向对象编程)。
我读过阿姆斯特朗的“艾朗”一书。这很艰难。我有一个小项目要做,但我一直在努力。
这个项目失败了,但几个月后,我已经充分地绘制了我脑海中的一切,我不再像以前那样在对象中思考。
我确实经历了一个将对象映射到erlang进程的阶段,但这并不是很长时间,我就走出了这个阶段。
现在,切换模式就像切换语言,或者从一辆车到另一辆车。开我父亲的车和开我的卡车感觉不一样,但用不了多久就会再次习惯。
我认为在Python中工作可能会阻碍您的工作,我强烈建议您查看erlang。它的语法非常陌生--但这也很好,因为更传统的语法(至少对我来说)会导致尝试用旧方法对它进行编程,并会感到沮丧。
发布于 2011-06-01 01:37:15
就我个人而言,我认为问题在于您试图使用函数式编程来解决为命令式编程而设计/陈述的问题。三种流行的范例(函数式、命令式、面向对象)各有长处:
input/result.
中的实体之间的关系
因此,当您处理一个问题时,第一个业务顺序是重新表述它,以便预期的范例可以正确地解决它。顺便说一句,据我所知,作为一个边节点,没有“纯OOP”这回事。您的OOP类(无论是Java、C#、C++、Python还是Objective C)的方法中的代码都是必须的。
回到你的例子:你陈述问题的方式(首先,然后,也,最后)本质上是强制性的。因此,构造函数解决方案几乎是不可能的(也就是说,如果不做副作用或单体之类的技巧)。类似地,即使您创建了一堆类,这些类本身也是无用的。要使用它们,您必须编写命令式代码(尽管这些代码嵌入在类中)来逐步解决问题。
重述问题:
图像输入:图像类型(完整或缩略图)、图像名称、所请求图像的文件system
从新的问题陈述中,您可以像这样解决它:
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和addThumbnailToF。
重要提示:创建一个新的文件系统听起来很大,但它不应该是大的。例如,如果这是一个更大的web服务器中的一个模块,那么“新文件系统”可以像指示新缩略图应该在哪里一样简单。或者,在最坏的情况下,它可以是将缩略图放到适当位置的IO monad。
https://stackoverflow.com/questions/6190745
复制相似问题