“策略模式”在Haskell [英] "Strategy Pattern" in Haskell

查看:147
本文介绍了“策略模式”在Haskell的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在OO世界中,我有一个类(我们称之为Suggestor),它实现了一些接近策略模式的内容,以便在运行时提供不同的算法实现。作为学习Haskell的一个练习,我想重写它。



实际用例非常复杂,所以我将简化一个例子。



假设我有一个类 Suggestedter ,它接受一系列规则,并将每个规则作为过滤器应用于列表数据库结果。

每个规则都有构建查询,后查询过滤器和记分员三个阶段。我们基本上最终得到了一个满足以下要求的接口:

  buildQuery :: Query  - >查询
postQueryFilter :: [记录] - > [记录]
记分员:: [记录] - > [(Record,Int)]

Suggestor需要一个匹配此接口的规则列表 - 动态在运行时 - 然后按顺序执行它们。 buildQuery()必须首先在所有规则中运行,然后是postQueryFilter,然后是scorer。 (即我不能只将一个规则的函数组合到一个函数中)。

在scala中的

我简单地执行

  //没有状态,所以一个单独的`object`而不是一个类是可以的
object Rule1 extends Rule {
def buildQuery ...
def postQueryFilter ...
def scorer ...
}

对象Rule2扩展Rule {....}

然后可以通过传递相关规则(基于用户输入在运行时定义)来初始化服务。

  val suggester =新建议者(列表(Rule1,Rule2,Rule3)); 

如果规则是单个函数,那么这很简单 - 只需传递函数列表即可。但是,由于每个规则实际上是三个函数,所以我需要以某种方式将它们组合在一起,所以我有多个实现符合接口。

我的第一个想法是类型类,但是这些看起来并不能满足我的需求 - 他们期待一个类型变量,并强制我的每个方法都必须使用它 - 它们不会。

 类`Rule`没有参数

我的第二个想法是放置每个都在haskell模块中,但由于模块不是First Class,所以我无法直接将它们传递给它们(而且它们当然不会强制执行接口)。

第三,我尝试创建一个记录类型来封装函数。

 数据Rule = Rule {buildQuery :: Query  - > Query,.... etc} 

然后为每个定义一个Rule实例。当它在每个模块中完成时,它封装得很好,工作正常,但感觉像是黑客,我不确定这是否适合使用haskell中的记录?



tl; dr - 如何将一组函数封装在一起,以便我可以将它们作为匹配接口的某个实例传递,但实际上并不使用类型变量。



或者我完全从错误的思维模式来到这里? 只需生成一个 Rule 键入,就像你一样

  data规则=规则
{buildquery ::查询 - >查询
,postQueryFilter :: [记录] - > [记录]
,记分员:: [记录] - > [(Record,Int)]
}

构建一个通用的应用程序方法 - m假设这些通用的东西存在,因为这些 Rules 被设计为独立于SQL结果运行

  applyRule :: Rule  - >结果 - >结果

最后,您可以随心所欲地实施任意数量的规则:只需导入 Rule 键入并创建一个适当的值。没有先验的理由可以像在OO设置中那样给每个不同的规则设置自己的类型。

  easyRule :: Rule 
easyRule =规则ID id(\recs - > zip recs [1 ..])

upsideDownRule :: Rule
upsideDownRule =规则反向反转(\recs - > ; zip recs [-1,-2 ..])

然后如果你有一个 Rule s你可以按顺序应用它们全部

  applyRules :: [Rule ]  - >结果 - >结果
applyRules [] res = res
applyRules(r:rs)res = applyRules rs(applyRule r res)

这实际上只是一个 foldr 的伪装

  applyRules rs res = foldr applyRule res rs 

foo :: Results - >结果
foo = applyRules [Some.Module.easyRule,Some.Other.Module.upsideDownRule]


In the OO world, I have a class (let's call it "Suggestor") that implement something approaching a "Strategy Pattern" to provide differing implementations of an algorithm at runtime. As an exercise in learning Haskell, I want to rewrite this.

The actual use-case is quite complex, so I'll boil down a simpler example.

Let's say I have a class Suggester that's takes a list of rules, and applies each rule as a filter to a list of database results.

Each rule has three phases "Build Query", "Post Query Filter", and "Scorer". We essentially end up with an interface meeting the following

buildQuery :: Query -> Query
postQueryFilter :: [Record] -> [Record]
scorer :: [Record] -> [(Record, Int)]

Suggestor needs to take a list of rules that match this interface - dynamically at run time - and then execute them in sequence. buildQuery() must be run across all rules first, followed by postQueryFilter, then scorer. (i.e. I can't just compose the functions for one rule into a single function).

in the scala I simply do

// No state, so a singleton `object` instead of a class is ok
object Rule1 extends Rule {
  def buildQuery ...
  def postQueryFilter ...
  def scorer ...
}

object Rule2 extends Rule { .... }

And can then initialise the service by passing the relevant rules through (Defined at runtime based on user input).

val suggester = new Suggester( List(Rule1, Rule2, Rule3) );

If the rules were a single function, this would be simple - just pass a list of functions. However since each rule is actually three functions, I need to group them together somehow, so I have multiple implementations meeting an interface.

My first thought was type classes, however these don't quite seem to meet my needs - they expect a type variable, and enforce that each of my methods must use it - which they don't.

No parameters for class `Rule`

My second thought was just to place each one in a haskell module, but as modules aren't "First Class" I can't pass them around directly (And they of course don't enforce an interface).

Thirdly I tried creating a record type to encapsulate the functions

data Rule = Rule { buildQuery :: Query -> Query, .... etc }

And then defined an instance of "Rule" for each. When this is done in each module it encapsulates nicely and works fine, but felt like a hack and I'm not sure if this is an appropriate use of records in haskell?

tl;dr - How do I encapsulate a group of functions together such that I can pass them around as an instance of something matching an interface, but don't actually use a type variable.

Or am I completely coming at this from the wrong mindset?

解决方案

Just generate a single Rule type as you did

data Rule = Rule
  { buildQuery :: Query -> Query
  , postQueryFilter :: [Record] -> [Record]
  , scorer :: [Record] -> [(Record, Int)]
  }

And build a general application method—I'm assuming such a generic thing exists given that these Rules are designed to operate independently over SQL results

applyRule :: Rule -> Results -> Results

Finally, you can implement as many rules as you like wherever you want: just import the Rule type and create an appropriate value. There's no a priori reason to give each different rule its own type as you might in an OO setting.

easyRule :: Rule
easyRule = Rule id id (\recs -> zip recs [1..])

upsideDownRule :: Rule
upsideDownRule = Rule reverse reverse (\recs -> zip recs [-1, -2..])

Then if you have a list of Rules you can apply them all in order

applyRules :: [Rule] -> Results -> Results
applyRules []     res = res
applyRules (r:rs) res = applyRules rs (applyRule r res)

which is actually just a foldr in disguise

applyRules rs res = foldr applyRule res rs

foo :: Results -> Results
foo = applyRules [Some.Module.easyRule, Some.Other.Module.upsideDownRule]

这篇关于“策略模式”在Haskell的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆