【问题标题】:How to write a Debounce() procedure in Delphi?如何在 Delphi 中编写 Debounce() 过程?
【发布时间】:2014-02-21 09:20:21
【问题描述】:

我想编写一个类似于通常在 Javascript 中实现的 Debounce 程序。例如debounce function in Underscore.js

我认为它可能看起来像这样:

procedure Debounce(const TimeMS : integer; MyAnonymousProcedure : TProc);

并且可以像这样使用:

begin
  Debounce(200, procedure
  begin
    //Do something here...
  end);
end;

可能的实施 #1

Debounce() 过程将检查自调用目标方法以来的时间。如果目标方法调用在 X 时间内被调用,则会延迟。

伪代码版本:

procedure Debounce(const TimeMS : integer; MyAnonymousProcedure : TProc);
var
  TimeSinceCalled : integer;
begin
  TimeSinceCalled := FindTimeSinceProcedureLastCalled(MyAnonymousProcedure);
  if TimeMS < TimeSinceCalled then
  begin
    // The procedure hasn't been called recently,
    SaveTimeOfProcedureCall(MyAnonymousProcedure); // so save the current time...
    MyAnonymousProcedure;                          //  ...and call the procedure.
  end else
  begin
    // The procedure has been called recently so we use a timer
    // to call the procedure in X milliseconds.
    // The timer is reset each time Debounce() is called
    // so procedures will only be called when the Debounce time-out has
    // be passed.
    GlobalTimer.Reset;
    GlobalTimer.CallProcedureIn(MyAnonymousProcedure, TimeMS);
  end;
end;

我面临的问题是我需要一些方法来识别目标过程。我的第一个想法是使用目标过程的方法地址作为ID。例如:
ID := Address(Foo); ID := Addr(Foo);
但这不适用于匿名方法,因为它们在我的测试中使用相同的地址。理想情况下,ID 应该是自动生成的。有任何想法吗?

【问题讨论】:

    标签: delphi anonymous-methods


    【解决方案1】:

    你说你所有的匿名程序的地址都是一样的,但那是不可能的。相反,我怀疑你正在尝试错误的值。你说你打电话给Address,但是没有这个功能。您可能指的是Addr,它与@ 运算符相同。但是,这会为您提供 MyAnonymousProcedure 形式参数的地址,而不是存储在其中的地址。很有可能每次调用函数时参数的地址都相同。

    您正在寻找一种方法来识别不同的过程引用。引用本身就足够了。您可以使用TDictionary&lt;TProc, TDateTime&gt; 将程序映射到它们的最后调用时间。

    危险在于每次运行时使用过程字面量调用 Debounce 函数,最终可能会构造该过程对象的全新实例,因此它的地址将不同于任何先前构造的过程,甚至如果它使用相同的代码和相同的捕获变量值。

    JavaScript 版本容易出现同样的问题,这就是它根本不尝试识别函数的原因。相反,它返回一个 新函数 来包装传入的函数。新函数添加了延迟行为。在 Delphi 中,它可以像这样工作:

    function Debounce(Delay: TDateTime; Proc: TProc): TProc;
    var
      LastCall: TDateTime;
    begin
      LastCall := 0;
      Result := procedure
      begin
        if LastCall + Delay <= Now then
          Proc;
        LastCall := Now;
      end;
    end;
    

    此版本的工作方式与下划线版本中的immediate 参数为真一样。这是一个简单的案例,因为它不需要任何延迟执行的机制。但是,它表明您实际上并不需要一种区分不同过程引用的方法,而这正是您之前所坚持的。

    【讨论】:

    • 我尝试使用TDictionary&lt;TProc, TDateTime&gt;,但Dictionary.Contains(aProc) 函数总是会在它看似不应该返回的情况下返回false。我使用整数作为字典键来测试我的代码,它工作正常,所以我不确定那里发生了什么。
    • 如果不使用 Addr() 或 @ 函数,如何获取 MyAnonymousProcedure 引用的例程的地址?
    • 我喜欢你的 Debounce() 实现。它非常简洁。但是,我希望避免在调用位置维护对某个例程的去抖动版本的引用。因此尝试根据匿名方法输入自动创建某种 ID。
    • 正如我所说,每次使用过程文字(在其中编写 procedure begin ... end)调用函数时,都会创建一个具有新标识的新过程,因此字典报告它是正确的还没有看到那个例子。将引用存储在变量中,然后测试变量是否存在。您将其与 JavaScript 进行比较,在该语言中,您需要存储 debounce 函数的结果;在 Delphi 中不会有任何不同。
    猜你喜欢
    • 2010-12-02
    • 2021-10-27
    • 2013-07-03
    • 1970-01-01
    • 1970-01-01
    • 2010-11-09
    • 2015-11-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多