【问题标题】:Call a random method, not twice调用随机方法,而不是两次
【发布时间】:2018-05-14 07:46:40
【问题描述】:

我有几种方法可以伪造一个单词,我需要随机调用它们,直到实现创建“人为错误”。

我想调用一个随机方法,然后再调用另一个,直到没问题,但绝不会重复两次。
将它们放在一个数组(或列表)中意味着我每次尝试一个方法时都会重写一个数组,这是一个丑陋的计算复杂性,我想编写“Scala 风格”的代码,尽量减少 var 和可变性。

编辑:

Oleg Pyzhcov solution 运行良好,但现在我有一些带有字符串参数的函数,而其他没有。 如何将函数及其调用参数存储在集合中?

val rand: Random = new Random()

def m1(a: String): Boolean = rand.nextBoolean()
def m2(): Boolean = rand.nextBoolean()
def m3(a: String, b: String): Boolean = rand.nextBoolean()
def m4(): Boolean = rand.nextBoolean()

def tryUntilOk(): Unit = {
  def out = rand.shuffle(Stream(m1 _, m2 _, m3 _, m4 _))
    .map(method => method()) // calling without params so error
    .find(result => result) // stop when a method return true
}

编辑 2:
详情

我有几种方法试图伪造一个词,但不能保证他们能做到。有的方法取动词的语气和时态,改变时态或语气,有的只取单词正确的写法去掉一些字母,还有的取名词的性和数来改变它的性。

我想在所有可能的方法中调用一个随机方法,如果它不能伪造单词(例如给定的名词仅以女性形式存在)然后随机调用另一个。重复这个操作,直到没有更多的方法可用,所以我们放弃了。 Oleg的解决方案对于随机部分很好,但是我找不到如何给方法调用参数。

具体例子:

package Counterfeiters

import Words.Noun

object Noun extends Counterfeiter[Noun] {
  override def counterfeit(word: Noun): Unit = {
    // For now I call methods without random order

    // This one take only one String param
    // And split letters that are duplicated like boot -> bot
    word.currentWriting = splitDoubleLetters(word.correctWriting) 

    // Then we compare word.currentWriting and word.correctWriting
    // If the word wasn't containing double letters, it isn't counterfeited
    if (!word.isCounterfeited) 
      // This one take 5 String params
      // And counterfeit the gender or the number of the word, randomly
      word.currentWriting = counterfeitGenderNumberWord("N", word.correctWriting, word.lemma, word.gender, word.number) 
  }
}

要应用 Oleg 的解决方案,我只需要找到如何将方法存储在集合中,并带有相应的参数。在这种情况下(splitDoubleLetters, (word.correctWriting)) 和(counterfeitGenderNumberWord, ("N", word.correctWriting, word.lemma, word.gender, word.number))。

解决方案

我按照 Oleg 在评论中的建议做了:

object Noun extends Counterfeiter[Words.Noun] {
  override def counterfeit(word: Words.Noun): Unit = {
    if (word.isCounterfeited) return

    def split: () => String = () => splitDoubleLetter(word.correctWriting)

    def ctftGenderNumber: () => String = () => counterfeitGenderNumberWord("N", word.correctWriting, word.lemma, word.gender, word.number)

    val methods: immutable.Seq[() => String] = Stream(split, ctftGenderNumber)

    val res: Option[String] = randomizer.shuffle(methods) // Shuffle methods
      .map(method => method()) // Call them one by one
      .find(result => result != word.correctWriting) // Until one counterfeit the word

    word.currentWriting = res match {
      case None => word.correctWriting // If every methods failed
      case _ => res.get
    }
  }
}

SergGr explained 一个可能的架构,我会很清楚地适应这个。
如果您想更好地了解我的工作,可以在 GitHub 上找到我的完整项目代码。

【问题讨论】:

  • VNourdin,不清楚你想要实现什么。您可能应该与我们分享您的“丑陋”代码,以便我们可以改进它以保留逻辑。特别是,该代码中的函数调用参数来自哪里?您只需要将您的方法转换为部分应用的函数吗?是别的吗?
  • 好的,我试着澄清一下并举个具体的例子。

标签: scala function random methods


【解决方案1】:

我同意 Oleg 的观点,即您需要将所有方法转换为相同形状的集合。我假设在word 包中,您有基类Word,其中包含具有不同特征的不同语音部分的子类。像这样的:

abstract class Word(val correctWriting: String, var currentWriting: String, val lemma: String) {

  def isCounterfeited: Boolean = !correctWriting.equals(currentWriting)

}

sealed trait Gender
case object Masculine extends Gender
case object Feminine extends Gender
case object Neutral extends Gender

sealed trait Number
case object Singular extends Number
case object Plural extends Number

class Noun(correctWriting: String, currentWriting: String, lemma: String, val gender: Gender, val number: Number) extends Word(correctWriting, currentWriting, lemma) {

}

你有 trait Counterfeiter 定义为

trait Counterfeiter[-W <: Word] {
  def counterfeit(word: W): Unit
}

然后你可以定义帮助类RandomCompoundCounterfeiter

type SimpleCounterfeiter[W <: Word] = (String, W) => String

class RandomCompoundCounterfeiter[W <: Word](val children: Seq[SimpleCounterfeiter[W]]) extends Counterfeiter[W] {
  override def counterfeit(word: W): Unit = {
    Random.shuffle(children).takeWhile(c => {
      word.currentWriting = c(word.correctWriting, word)
      !word.isCounterfeited
    })
  }
}

RandomCompoundCounterfeiter 是完成您要求的主要工作的类:它以随机顺序应用其他 SimpleCounterfeiter。它首先洗牌children(即真正的造假者)列表并应用它们,直到某些word.isCounterfeited 最终成为true 或列表已用尽。

请注意,RandomCompoundCounterfeiter 在每次通话中都会重新洗牌造假者。如果您想让您的顺序在应用程序的不同运行之间有所不同,但在单次运行中的不同单词相同,只需将改组移至构造函数即可。

现在定义SimpleCounterfeiters 的基本功能列表,例如

object Noun {

  val singularCounterfeiter = (correctWriting: String, word: Noun) => {
    if (word.number == Singular)
      correctWriting
    else
      ???
  }

  val pluralCounterfeiter = (correctWriting: String, word: Noun) => {
    if (word.number == Plural)
      correctWriting
    else
      ???
  }


  def genderCounterfeiter(newGender: Gender): SimpleCounterfeiter[Noun] = (correctWriting: String, word: Noun) => {
    if (word.gender == newGender)
      correctWriting
    else
      ???
  }


  val all = List(
    GenericCounterfeiters.splitDoubleLetters,
    singularCounterfeiter,
    pluralCounterfeiter,
    genderCounterfeiter(Neutral),
    genderCounterfeiter(Masculine),
    genderCounterfeiter(Feminine))

  val nounCounterfeiter = new RandomCompoundCounterfeiter[Noun](all)
}

现在您可以使用Noun.nounCounterfeiter 作为您的随机Counterfeiter[Noun]。这里的主要思想是使每个原子伪造者具有相同的形状,这是通过始终将整个Word(或其子类)传递给该方法来实现的。所以现在每个方法都可以在需要时访问所有相关信息。

如果您希望将伪造者中的典型if 条件移动到一个地方,您可以将您的代码重构为更面向 OOP 的方式:

class RandomCompoundCounterfeiter[W <: Word](val children: Seq[Counterfeiter[W]]) extends Counterfeiter[W] {
  override def counterfeit(word: W): Unit = {
    Random.shuffle(children).takeWhile(c => {
      c.counterfeit(word)
      !word.isCounterfeited
    })
  }
}

trait SimpleCounterfeiter[-W <: Word] extends Counterfeiter[W] {
  override def counterfeit(word: W): Unit = {
    if (isApplicable(word))
      word.currentWriting = counterfeitImpl(word.correctWriting, word)
  }

  def isApplicable(word: W): Boolean

  def counterfeitImpl(correctWriting: String, word: W): String
}

object GenericCounterfeiters {
  val splitDoubleLetters = new SimpleCounterfeiter[Word] {
    override def isApplicable(word: Word) = true

    override def counterfeitImpl(correctWriting: String, word: Word) = ???
  }
}

object Noun {

  val singularCounterfeiter = new SimpleCounterfeiter[Noun] {
    override def isApplicable(word: Noun) = word.number != Singular

    override def counterfeitImpl(correctWriting: String, word: Noun) = ???
  }

  val pluralCounterfeiter = new SimpleCounterfeiter[Noun] {
    override def isApplicable(word: Noun) = word.number != Plural

    override def counterfeitImpl(correctWriting: String, word: Noun) = ???
  }


  def genderCounterfeiter(newGender: Gender) = new SimpleCounterfeiter[Noun] {
    override def isApplicable(word: Noun) = word.gender != newGender

    override def counterfeitImpl(correctWriting: String, word: Noun) = ???
  }

  val all = List(
    GenericCounterfeiters.splitDoubleLetters,
    singularCounterfeiter,
    pluralCounterfeiter,
    genderCounterfeiter(Neutral),
    genderCounterfeiter(Masculine),
    genderCounterfeiter(Feminine))

  val nounCounterfeiter = new RandomCompoundCounterfeiter[Noun](all)
}

【讨论】:

  • 我也同意奥列格并做到了。我是 Scala 的初学者,我没有你建议的那么好。我可能会改变我的概念,它比我所做的更好。有兴趣的可以看我的 GitHub。感谢您提供解释清楚的帮助。
【解决方案2】:

使用Stream 表示懒惰,使用Random#shuffle 方法可以得到:

import scala.util.Random

def m1(): Boolean = Random.nextBoolean()
def m2(): Boolean = Random.nextBoolean()
def m3(): Boolean = Random.nextBoolean()
def m4(): Boolean = Random.nextBoolean()

def out = Random.shuffle(Stream(m1 _, m2 _, m3 _, m4 _))
  .map(method => method()) // pass in any necessary params
  .find(result => !result) // do your check

这里,out 的类型为 Option[Boolean],因为方法 m1-m4 都返回 Boolean

【讨论】:

  • 我有不同参数类型和数字的方法,我怎样才能很好地传递它们?
  • 这只是为了简化,我们不在乎我的方法到底是什么。关键是,我有几种方法,试图做一个动作(在这种情况下返回 true),它们有不同的参数。我想随机调用其中一个,然后在操作失败时再调用另一个。
  • @VNourdin 只需将它们包装在 Function0 中:() =&gt; m1("foo", 42) 同时将它们传递到流中
猜你喜欢
  • 2020-01-20
  • 1970-01-01
  • 2019-08-06
  • 1970-01-01
  • 1970-01-01
  • 2019-11-07
  • 1970-01-01
  • 2011-06-19
  • 1970-01-01
相关资源
最近更新 更多