在Scala中的函数式编程示例

10 浏览
0 Comments

在Scala中的函数式编程示例

我正在学习Scala。由于Odersky和其他作者的伟大工作,它非常有前景。\n我选择了一个欧拉问题(http://projecteuler.net/)作为一个最小的例子。我正在尝试以函数式的方式解决问题。所以这不是一个“请立即回答我,否则我的老板会杀了我”的问题,而是一个“如果你有时间,你能帮助一个习惯于命令式语言的程序员进入函数式世界吗?”的问题?\n问题:我想要一个用于扑克牌的类。扑克牌由0到5张卡组成。我想一次性地构建卡牌列表,也就是说:我的Hand类将是不可变的,如果我想添加一张牌,那么我就会创建一个新的Hand对象。\n因此,我需要一个可以作为“val”创建的Card集合,而不是作为“var”创建。\n第一步:构造函数,每个卡牌数量一个。但是Card的集合在每个构造函数中被处理,所以我必须将其设置为“var”!\n下面是代码,Card类只是一个Suit和一个Value,以字符串形式传递给构造函数(“5S”表示黑桃5):\n

class Hand(mycards : List[Card]) {
  // 这个应该是val,我猜
  private var cards : List[Card] = {
    if (mycards.length>5)
      throw new IllegalArgumentException(
        "Illegal number of cards: " + mycards.length);
    sortCards(mycards)
  }
  // 完整的手牌构造函数
  def this(a : String, b : String, c : String, d : String, e : String) = {
      this(Nil)
      // 分配卡牌
      val cardBuffer = new ListBuffer[Card]()
      if ( a!=null ) cardBuffer += new Card(a)
      if ( b!=null ) cardBuffer += new Card(b)
      if ( c!=null ) cardBuffer += new Card(c)
      if ( d!=null ) cardBuffer += new Card(d)
      if ( e!=null ) cardBuffer += new Card(e)
      cards = sortCards(cardBuffer.toList)
  }
  // 少于5张卡牌的手牌
  def this(a : String, b : String, c : String, d : String) = this(a,b,c,d,null)
  def this(a : String, b : String, c : String) = this(a, b, c, null)
  def this(a : String, b : String) = this(a, b, null)
  def this(a : String) = this(a, null)
  def this() = this(Nil)
/* removed */
}

\n你知道如何以真正的函数式方式实现吗?\n谢谢。\nPS:如果你真的想知道,这是第54个问题。

0
0 Comments

函数式编程在Scala中的一个例子

在这个例子中,你只被允许使用五张牌,我会在编译时使用Tuple5来检查:

type HandType = (ACard, ACard, ACard, ACard, ACard)
case class Hand(h: HandType)
abstract class ACard {
  def exists: Boolean
}
case class Card(value: Int, color: Color) extends ACard {
  def exists = true
}
case object NoCard extends ACard {
  def exists = false
}
abstract class Color(val c: Int)
case object H extends Color(1)
case object C extends Color(2)
case object S extends Color(3)
case object D extends Color(4)
case object NoColor extends Color(0)
implicit def tuple2Card(t: (Int, Color)) = Card(t._1, t._2)
val h1 = Hand((Card(4, H), Card(6, S), Card(2, S), Card(8, D), NoCard))
val h2 = Hand((4 -> H, 6 -> S, 2 -> S, 8 -> D, NoCard))
println(h1)
println(h2)
h1.h.productIterator foreach { c => println(c.asInstanceOf[ACard].exists) }

当然,在另一个例子中,当元素的数量不确定时,你需要在运行时检查它们。`productIterator`只返回一个`Iterator[Any]`,但当你直接使用字段标识符(_1.._5)来使用你的牌时,你将得到一个`ACard`。

使用元组是有趣的,即使-nom-nom解决方案更简洁。但是你的解决方案只提供了带有5个参数的完整构造函数,这可能是一个问题。这个例子要求计算手牌的等级,而我已经设法这样做:搜索一对牌的等级,如果有这样的等级,从手牌中提取这对牌,并在剩下的牌上迭代。为了创建子手牌,我需要用变量数量的NoCard调用完整的构造函数。也许这不是很难,但是一个可变参数构造函数似乎是一个完美的选择。

如果有人真的想要表示在编译时检查的有限长度列表,一个更好的选择是像`Option[(Card, Option[(Card, Option[(Card, Option[(Card, Option[Card])])])])`这样的东西

虽然上面的代码在没有一些类型级别的魔法的情况下看起来很丑陋,但是通过简单的类型别名,它仍然可以缩短为`OC[OC[OC[OC[OC[Unit]]]]]`。

0
0 Comments

在上面的代码中,问题出在没有从主构造函数初始化cards变量。解决方法是修复辅助构造函数,将传入的参数转换成选项(Option),然后使用flatten方法去除None,并将Some转换回原来的类型。

// this should be val, I guess
private var cards : List[Card] = {
  if (mycards.length>5)
    throw new IllegalArgumentException(
      "Illegal number of cards: " + mycards.length);
  sortCards(mycards)
}
// full hand constructor
def this(a : String, b : String, c : String, d : String, e : String) = {
    this(Nil)
    // assign cards
    val cardBuffer = new ListBuffer[Card]()
    if ( a!=null ) cardBuffer += new Card(a)
    if ( b!=null ) cardBuffer += new Card(b)
    if ( c!=null ) cardBuffer += new Card(c)
    if ( d!=null ) cardBuffer += new Card(d)
    if ( e!=null ) cardBuffer += new Card(e)
    cards = sortCards(cardBuffer.toList)
}
def this(a : String, b : String, c : String, d : String, e : String) = 
    this(List(a, b, c, d, e).map(Option(_)).flatten.map(Card(_)))

0
0 Comments

在这个例子中,问题的出现是因为需要在Hand类中限制手牌数量不能超过五张。解决方法是使用辅助构造函数和require函数来实现。

首先,辅助构造函数中的参数String*表示可以接受任意数量的字符串参数。然后使用map函数将每个字符串参数转换为一个Card对象,并将结果传递给父构造函数。父构造函数中的require函数用来检查手牌数量是否超过了五张。

在Hand类中,mycards属性的类型是List[Card],它等价于cards: List[Card],即将传递给构造函数的参数赋值给mycards属性。

如果尝试创建超过五张手牌,就会抛出java.lang.IllegalArgumentException异常,并显示错误消息"can't be more than five cards"。

通过这种方式,可以简洁地实现对手牌数量的限制。

以下是完整的代码示例:

class Card(s: String) {
  println("I'm a: " + s)
}
class Hand(val mycards: List[Card]) {
  require(mycards.size <= 5, "can't be more than five cards")
  def this(input: String*) = {
    this(input.map(new Card(_)).toList)
  }
}
val hand = new Hand("one", "two", "three", "four", "five", "six")

运行以上代码会输出以下结果:

I'm a: one
I'm a: two
I'm a: three
I'm a: four
I'm a: five
I'm a: six
Exception in thread "main" java.lang.IllegalArgumentException: requirement failed: can't be more than five cards
    at scala.Predef$.require(Predef.scala:224)
    at Hand.(Main.scala:6)
    at Main$.main(Main.scala:14)
    at Main.main(Main.scala)

可以看到,当手牌数量超过五张时,会抛出异常并显示错误消息"can't be more than five cards"。

0