Function 对自定义scala递归预防机制的改进

Function 对自定义scala递归预防机制的改进,function,scala,recursion,locking,Function,Scala,Recursion,Locking,我想创建一个智能递归预防机制。我希望能够以某种方式对一段代码进行注释,以标记它不应该在递归中执行,如果它确实在递归中执行,那么我希望抛出一个自定义错误(可以捕获该错误以允许在发生这种情况时执行自定义代码) 以下是我的尝试,直到这里: import scala.collection.mutable.{Set => MutableSet, HashSet => MutableHashSet } case class RecursionException(uniqueID:Any) ex

我想创建一个智能递归预防机制。我希望能够以某种方式对一段代码进行注释,以标记它不应该在递归中执行,如果它确实在递归中执行,那么我希望抛出一个自定义错误(可以捕获该错误以允许在发生这种情况时执行自定义代码)

以下是我的尝试,直到这里:

import scala.collection.mutable.{Set => MutableSet, HashSet => MutableHashSet }

case class RecursionException(uniqueID:Any) extends Exception("Double recursion on " + uniqueID)

object Locking {
  var locks:MutableSet[Any] = new MutableHashSet[Any]

  def acquireLock (uniqueID:Any) : Unit = {
    if (! (locks add uniqueID))
      throw new RecursionException(uniqueID)
  }

  def releaseLock (uniqueID:Any) : Unit = {
    locks remove uniqueID
  }

  def lock1 (uniqueID:Any, f:() => Unit) : Unit = {
    acquireLock (uniqueID)
    try {
      f()
    } finally {
      releaseLock (uniqueID)
    }
  }

  def lock2[T] (uniqueID:Any, f:() => T) : T = {
    acquireLock (uniqueID)
    try {
      return f()
    } finally {
      releaseLock (uniqueID)
    }
  }
}
现在要锁定一个代码段,我需要:

import Locking._

lock1 ("someID", () => {

  // Custom code here

})
我的问题是:

  • 是否有任何明显的方法可以消除硬编码唯一标识符的需要?我需要一个唯一标识符,它将在包含锁定部分的函数的所有调用之间共享(所以我不能用计数器来生成唯一的值,除非scala有静态函数变量)
  • 有什么方法可以美化匿名函数的语法吗?具体来说,就是让我的代码看起来像
    lock1(“id”){/*code在这里*/}
    或任何其他更漂亮的外观
  • 在这个阶段问有点傻,但我还是会问-我是在重新发明轮子吗?(也就是说,这样的东西存在吗?)
  • 疯狂的最后一个想法:我知道滥用
    synchronized
    关键字(至少在java中)会导致代码只执行一次(在这个意义上,没有多个线程可以同时进入代码的这一部分)。我不认为这会阻止同一线程执行两次代码无论如何,如果它确实阻止了它,我仍然不想要它(即使我的程序是单线程的),因为我很确定它会导致死锁,并且不会报告异常

    编辑:为了让它更清楚,这个项目是为了调试错误和学习scala。它除了在运行时轻松查找代码错误(用于检测不应该发生的递归)之外没有任何实际用途。请参阅本文的注释

    是否有任何明显的方法可以消除硬编码唯一标识符的需要?

    由于您在评论中所说的不是prod代码,我想您可以使用如下函数
    hashCode
    属性:

    def lock1 (f:() => Unit) : Unit = {
        acquireLock (f.hashCode)
        try {
          f()
        } finally {
          releaseLock (f.hashCode)
        }
    
    def lock1 (uniqueID:Any)(f:() => Unit) : Unit = {
    
    有没有办法美化匿名函数的语法?

    通过前面提到的更改,语法应该更漂亮:

    lock1 {
    
    如果您计划保留标识符(如果
    hashcode
    没有为您剪切它),您可以这样定义您的方法:

    def lock1 (f:() => Unit) : Unit = {
        acquireLock (f.hashCode)
        try {
          f()
        } finally {
          releaseLock (f.hashCode)
        }
    
    def lock1 (uniqueID:Any)(f:() => Unit) : Unit = {
    
    这将允许您使用以下命令调用
    lock1
    方法:

    lock("foo") {
    
    }
    

    干杯!

    不太清楚你的目标,但有几句话:

    首先,您不需要使用lock1和lock2来区分Unit和其他类型。Unit是正确的值类型,泛型方法也适用于它。此外,您可能应该使用按名称调用参数=>T,而不是函数()=>T,并使用两个参数列表:

    def lock[T] (uniqueID:Any)(f: => T) : T = {
      acquireLock (uniqueID)
      try {
        f
      } finally {
        releaseLock (uniqueID)
      }
    }
    
    然后您可以使用
    lock(id){block}
    调用,它看起来像是if或synchronized之类的公共指令

    第二,为什么您需要一个唯一的ID,为什么要将Lock设置为singleton?相反,将
    Lock
    设置为一个类,并且拥有与您拥有ID一样多的实例

    class Lock {
      def lock[T](f: => T): T = {acquireLock() ...}
    }
    
    (您甚至可以将锁方法命名为
    apply
    ,这样您就可以只执行
    myLock{…}
    而不是
    myLock.lock{…}

    撇开多线程不谈,现在只需要为
    acquire
    /
    releaseLock


    最后,如果您需要支持多线程,您必须决定几个线程是否可以进入锁(这不是递归)。如果可以,则应将布尔值替换为
    DynamicVariable[boolean]
    (或者可能是java
    ThreadLocal
    ,因为
    DynamicVariable
    InheritableThreadLocal
    ,您可能想要也可能不想要)。如果他们不能,你只需要在
    acquire
    /
    releaseLock

    中同步访问,不管你是否在重新发明轮子,为什么你想要这个轮子?防止递归的价值是什么?防止函数(ish)语言中的递归?为什么?@DonRoby:主要目的是代码测试(显然,它对编程没有什么好处)。我正在编写一个复杂的系统,其中许多函数在长链中相互调用,在编程时可能很容易混淆,并可能导致递归。如果出现指定此项的运行时错误,将比读取长堆栈跟踪更容易。除此之外,这看起来是练习scala的一个很好的方法:)你能解释一下这是如何防止递归的吗?@PabloFernandez:用它锁定整个函数体。试试
    def bad:Unit={lock1(“hi”,()=>bad)}
    然后运行
    bad
    。我同意这个机制不仅仅可以用来防止递归,但是递归预防是它为hashCode工作的解决方案创建的最初原因:)但是另一个给了我语法错误:
    def bad:Unit={lock1(“hi”){bad}
    返回
    :12:错误:类型不匹配;找到:所需单元:()=>单元
    。是否对此有scala版本限制?(使用2.8.1)有趣的一点是,单元作为一种类型,我不知道你能做到。除此之外,对于语法更正,请参阅下面Pablo回答中的错误。对于创建一个新类,它不会在每次调用函数时创建一个新的类实例吗?那么锁将如何在实例之间共享?当然,你会d不执行
    new Lock().Lock{…}
    ,这将不起任何作用。如果锁是特定于实例的,则将锁创建为类的成员,否则创建为全局对象。与使用同步锁的操作相同,至少当它不是
    时是如此。