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
})
我的问题是:
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]
(或者可能是javaThreadLocal
,因为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{…}
,这将不起任何作用。如果锁是特定于实例的,则将锁创建为类的成员,否则创建为全局对象。与使用同步锁的操作相同,至少当它不是此
时是如此。