Haskell:如何组织一组所有采用相同参数的函数

DJG*_*DJG 10 haskell dry

我正在编写一个程序,其中包含几个具有相同参数的函数.为简单起见,这是一个有点人为的例子:

buildPhotoFileName time word stamp = show word ++ "-" ++ show time ++ show stamp
buildAudioFileName time word = show word ++ "-" ++ show time ++ ".mp3"
buildDirectoryName time word = show word ++ "_" ++ show time
Run Code Online (Sandbox Code Playgroud)

假设我在IO上循环一个资源以在运行时获取timeword参数.在这个循环中,我需要加入上述函数的结果以便进一步处理,所以我这样做:

let photo = buildPhotoFileName time word stamp
    audio = buildAudioFileName time word
    dir   = buildDirectoryName time word
in ....
Run Code Online (Sandbox Code Playgroud)

这似乎违反了"不要重复自己"的原则.如果我在路上发现我想改成word函数word,我可能会在let表达式的开头创建一个新的绑定,如下所示:

let wrd   = processWord word
    photo = buildPhotoFileName time wrd stamp
    audio = buildAudioFileName time wrd
    dir   = buildDirectoryName time wrd
in ....
Run Code Online (Sandbox Code Playgroud)

并会在每次我写的时间来改变wordwrd,从而导致错误,如果我记得更改某些函数调用,而不是其他.

在OOP中,我会通过将上述功能中的一类,其构造函数会采取解决这个问题time,并word作为参数.实例化的对象基本上是cur time和的三个函数word.如果我想确保函数接收processWord word而不是word作为"参数",我可以调用processWord构造函数.

有什么更好的方法可以更适合函数式编程和Haskell?

Nik*_*kov 11

既然你说你已经准备好为此创建一个OO-wrapper-class,我假设你愿意改变你的功能.以下是一个函数,生成您想要的所有三个结果的元组:

buildFileNames time word stamp = 
  ( show word ++ "-" ++ show time ++ show stamp,
    show word ++ "-" ++ show time ++ ".mp3",
    show word ++ "_" ++ show time )
Run Code Online (Sandbox Code Playgroud)

你可以像这样使用它:

let wrd   = processWord word
    (photo, audio, dir) = buildFileNames time wrd stamp
    in ....
Run Code Online (Sandbox Code Playgroud)

如果你不需要任何结果,你可以像这样跳过它们:

let wrd   = processWord word
    (_, audio, _) = buildFileNames time wrd stamp
    in ....
Run Code Online (Sandbox Code Playgroud)

值得注意的是,您不必担心Haskell会浪费资源来计算您不使用的计算值,因为它很懒惰.


Dan*_*ner 7

你在OOP土地上描述的解决方案对我来说听起来像是一个很好的解决方案.以机智:

data UID = UID
    { _time :: Integer
    , _word :: String
    }
Run Code Online (Sandbox Code Playgroud)

在此记录中包括或不包括"标记"是一项设计决定,我们可能没有足够的信息在这里回答.可以将此数据类型放在其自己的模块中,并定义"智能构造函数"和"智能访问器":

uid = UID
time = _time
word = _word
Run Code Online (Sandbox Code Playgroud)

然后隐藏在模块边界,如真正的构造函数和存取出口UID型,uid智能构造,以及timeword智能存取,而不是UID构造函数或_time_word存取.

module UID (UID, uid, time, word) where
Run Code Online (Sandbox Code Playgroud)

如果我们后来发现智能构造函数应该进行一些处理,我们可以改变以下定义uid:

uid t w = UID t (processWord w)
Run Code Online (Sandbox Code Playgroud)


Joa*_*ner 6

建立在Nikita Vokov的答案之上,您可以使用记录通配符来获得一些简洁的语法,几乎不重复:

{-# LANGUAGE RecordWildCards #-}

data FileNames = FileNames { photo :: String, audio :: String, dir :: String }

buildFileNames :: Word -> Time -> Stamp -> FileNames
buildFileNames time word stamp = FileNames
  (show word ++ "-" ++ show time ++ show stamp)
  (show word ++ "-" ++ show time ++ ".mp3")
  (show word ++ "_" ++ show time )

let FileNames {...} = buildFileNames time wrd stamp
in ... photo ... audio ... dir...
Run Code Online (Sandbox Code Playgroud)