努力使用纯函数式编程来解决日常问题
我今天在黑客新闻中看到这篇文章。 我正在努力理解纯函数式编程如何帮助我抽象真实世界的问题。 7年前,我从必须转向OO编程。 我觉得我已经掌握了它,它为我服务的很好。 在过去的几年里,我学习了一些功能编程中的技巧和概念,比如map和reduce,我也喜欢它们。 我在OO代码中使用过它们,并对此感到满意,但是当抽象一组指令时,我只能想到OO抽象使代码更漂亮。
最近我一直在研究python的一个问题,我一直试图避免使用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的论文“野兽中的美丽:为Awkward Squad提供功能性语义”
改变你的思维方式的唯一方法就是改变你的思维方式。 我可以告诉你什么对我有用:
我想要开发一个需要并发性的个人项目。 我环顾四周,发现了erlang。 我选择它是因为我认为它对并发性有最好的支持,而不是出于任何其他原因。 我之前从未使用过一种功能语言(并且仅仅为了比较,我在20世纪90年代初开始进行面向对象编程。)
我读了阿姆斯特朗的erlang书。 这很艰难。 我有一个小型项目可以开展工作,但我一直在喋喋不休。
这个项目是失败的,但几个月后,我已经将我的头脑中的所有东西都映射到了足够的位置,以至于我不再像过去那样在对象中思考问题。
我确实经过了一个阶段,在这个阶段我将对象映射到erlang进程,但那不是太长时间,我摆脱了它。
现在,切换范例就像是切换语言,或者从一辆车转到另一辆车。 驾驶我父亲的车感觉与我的卡车不同,但不需要很长时间就可以再次习惯。
我认为用Python工作可能会阻碍你,我强烈建议你检查一下erlang。 它的语法非常陌生 - 但这也很好,因为更传统的语法(至少对我而言)会导致尝试用旧的方式编程并且感到沮丧。
就我个人而言,我认为问题在于您正在尝试使用函数式编程来解决针对命令式编程设计/陈述的问题。 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 monad将缩略图放到适当的位置。
链接地址: http://www.djcxy.com/p/42869.html上一篇: Struggling with using pure functional programming to solve an everyday problem