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

发布于 2024-12-11 19:29:33 字数 1752 浏览 0 评论 0原文

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

这是我在此之前的尝试:

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

})

我的问题是:

  1. 是否有任何明显的方法可以消除对唯一标识符进行硬编码的需要? strong> 我需要一个实际共享的唯一标识符在包含锁定部分的函数的所有调用之间(所以我不能有类似计数器的东西来生成唯一值,除非不知何故 scala 有静态函数变量)。我以某种方式思考
  2. 有没有办法美化匿名函数的语法?具体来说,有一些东西会让我的代码看起来像lock1(“id”){/*代码放在这里* / } 或任何其他更漂亮的外观。
  3. 在这个阶段问有点傻,但我还是会问 - 我是在重新发明轮子吗?(即是否存在这样的事情?)

疯狂的最终想法: > 我知道滥用 synchronized 关键字(至少在 java 中)可以保证代码只会执行一次(从某种意义上说,没有多个线程可以在同一时刻进入该部分代码)同一时间)。我不认为它会阻止同一个线程执行代码两次(尽管我在这里可能是错的)。不管怎样,如果它确实阻止了它,我仍然不想要它(即使我的程序是单线程的),因为我很确定它会导致死锁并且不会报告异常。

编辑:为了更清楚地说明,该项目用于错误调试目的和学习 scala。除了在运行时轻松查找代码错误(用于检测不应该发生的递归)之外,它没有真正的用途。请参阅这篇文章的评论。

I would like to create a smart recursion prevention mechanism. I would like to be able to annotate a piece of code somehow, to mark that it should not be executed in recursion, and if it is indeed executed in recursion, then I want to throw a custom error (which can be caught to allow executing custom code when this happens)

Here is my attempt until here:

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)
    }
  }
}

and now to lock a code segment I do:

import Locking._

lock1 ("someID", () => {

  // Custom code here

})

My questions are:

  1. Is there any obvious way to get rid of the need for hard coding a unique identifier? I need a unique identifier which will actually be shared between all invocations of the function containing the locked section (so I can't have something like a counter for generating unique values, unless somehow scala has static function variables). I thought on somehow
  2. Is there any way to prettify the syntax of the anonymouse function? Specifically, something that will make my code look like lock1 ("id") { /* code goes here */ } or any other prettier look.
  3. A bit silly to ask in this stage, but I'll ask anyway - Am I re-inventing the wheel? (i.e. does something like this exist?)

Wild final thought: I know that abusing the synchronized keyword (at least in java) can gaurantee that there would be only one execution of the code (in the sense that no multiple threads can enter that part of the code at the same time). I don't think it prevents from the same thread to execute the code twice (although I may be wrong here). Anyway, if it does prevent it, I still don't want it (even thoug my program is single threaded) since I'm pretty sure it will lead to a deadlock and won't report an exception.

Edit: Just to make it clearer, this project is for error debugging purposes and for learning scala. It has no real useage other than easily finding code errors at runtime (for detecting recursion where it shouldn't happen). See the comments to this post.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

作妖 2024-12-18 19:29:33

不太确定您的目标是什么,但有几点说明:

首先,您不需要执行 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。

第二,为什么需要uniqueId,为什么要把Lock做成单例?相反,让 Lock 成为一个类,并拥有与 id 一样多的实例。

class Lock {
  def lock[T](f: => T): T = {acquireLock() ...}
}

(您甚至可以将锁定方法命名为 apply,这样您就可以执行 myLock{....} 而不是 myLock.lock{...}< /code>)

抛开多线程不谈,你现在只需要一个布尔变量来acquire/releaseLock

最后,如果你需要支持多线程,你必须决定是否有多个线程可以进入锁(这不会是递归)。如果可以的话,布尔值应该替换为 DynamicVariable[Boolean] (或者可能是 java ThreadLocal,因为 DynamicVariable 是一个 InheritableThreadLocal,您可能想要也可能不想要)。如果不能,您只需在acquire/releaseLock中同步访问即可。

Not quite sure what you're aiming at, but a few remarks:

First, you do not need to do lock1 and lock2 to distinguish Unit and the other type. Unit is a proper value type, the generic method will work for it too. Also, you should probably use a call by name argument => T, rather than a function () => T, and use two argument lists:

def lock[T] (uniqueID:Any)(f: => T) : T = {
  acquireLock (uniqueID)
  try {
    f
  } finally {
    releaseLock (uniqueID)
  }
}

Then you can call with lock(id){block} and it looks like common instructions such as if or synchronized.

Second, why do you need a uniqueId, why make Lock a singleton? Instead, make Lock a class, an have as many instances as you would have had ids.

class Lock {
  def lock[T](f: => T): T = {acquireLock() ...}
}

(You may even name your lock method apply, so you can just do myLock{....} rather than myLock.lock{...})

Multithreading aside, you now just need a Boolean var for acquire/releaseLock

Finally, if you need to support multithreading, you have to decide whether several thread can enter the lock (that would not be recursion). If they can, the boolean should be replaced with a DynamicVariable[Boolean] (or maybe a java ThreadLocal, as DynamicVariable is an InheritableThreadLocal, which you may or may not want). If they cannot, you just need to synchronize access in acquire/releaseLock.

遥远的她 2024-12-18 19:29:33

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

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

def lock1 (f:() => Unit) : Unit = {
    acquireLock (f.hashCode)
    try {
      f()
    } finally {
      releaseLock (f.hashCode)
    }

有什么方法可以美化匿名函数的语法吗?

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

lock1 {

如果您打算保留标识符(如果 hashcode 不适合你)你可以像这样定义你的方法:

def lock1 (uniqueID:Any)(f:() => Unit) : Unit = {

这将让你调用 lock1 方法:

lock("foo") {

}

干杯!

Is there any obvious way to get rid of the need for hard coding a unique identifier?

Since for what you said on the comments this is not prod code, I guess you could use the functions hashCode property like this:

def lock1 (f:() => Unit) : Unit = {
    acquireLock (f.hashCode)
    try {
      f()
    } finally {
      releaseLock (f.hashCode)
    }

Is there any way to prettify the syntax of the anonymouse function?

With the before-mentioned change the syntax should be prettier:

lock1 {

If you're planning on keeping the identifier (if hashcode doesn't cut it for you) you can define your method like this:

def lock1 (uniqueID:Any)(f:() => Unit) : Unit = {

That will let you call the lock1 method with:

lock("foo") {

}

Cheers!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文