【问题标题】:How to do lazy evaluation in Dart?如何在 Dart 中进行惰性求值?
【发布时间】:2016-01-18 01:40:42
【问题描述】:

是否有本机(支持语言)惰性求值语法?类似 Scala 中的lazy val

我已经浏览了the docs,但找不到任何东西。关于“懒加载库”只有一章,但不是我要问的。

根据这项研究,我倾向于相信(如果我错了,请纠正我)目前没有这样的事情。但是,也许您知道将提供该功能的任何计划或功能请求?或者可能是被 Dart 团队考虑并拒绝了?

如果确实没有对此的本机支持,那么实现惰性求值的最佳实践(最佳语法)是什么?一个例子将不胜感激。

编辑:

我正在寻找的功能的好处与其他语言的实现大致相同:Scala's lazy valC#'s Lazy<T>Hack's __Memorize attribute

  1. 简洁的语法
  2. 延迟计算直到需要该值
  3. 缓存结果(按需懒惰)
  4. 不要破坏纯函数范式(解释如下)

一个简单的例子:

class Fibonacci {

  final int n;
  int _res = null;

  int get result {
    if (null == _res) {
      _res = _compute(this.n);
    }
    return _res;
  }

  Fibonacci(this.n);

  int _compute(n) {
    // ...
  }
}

main(List<String> args) async {
  print(new Fibonacci(5).result);
  print(new Fibonacci(9).result);
}

getter 非常冗长并且有重复的代码。 此外,我无法创建构造函数const,因为缓存变量_res 必须按需计算。我想如果我有一个类似 Scala 的 lazy 功能,那么我也会有语言支持来拥有一个常量构造函数。这要归功于这样一个事实,即懒惰评估的_resreferentially transparentwould not be in the way

class Fibonacci {

  final int n;
  int lazy result => _compute(this.n);

  const Fibonacci(this.n);  // notice the `const`

  int _compute(n) {
    // ...
  }
}

main(List<String> args) async {
  // now these makes more sense:
  print(const Fibonacci(5).result);
  print(const Fibonacci(9).result);
}

【问题讨论】:

  • 您能否提供一个具体示例,说明您希望使用惰性求值完成什么。据我所知,没有特殊的语言支持,也没有关于它的讨论。 List, Map, ... 上的许多方法都是惰性求值的。例如map()fold()reduce()、...参见例如stackoverflow.com/questions/20491777/dart-fold-vs-reduce。也许只是闭包做你想要的。
  • 您可以将您的 int get result 缩短为 int get result =&gt; _res ??= _compute(this.n);

标签: dart language-features


【解决方案1】:

更新2

来自@lrn 的评论 - 使用 Expando 进行缓存使其可以与 const 一起使用:

class Lazy<T> {
  static final _cache = new Expando();
  final Function _func;
  const Lazy(this._func);
  T call() {
    var result = _cache[this];
    if (identical(this, result)) return null;
    if (result != null) return result;
    result = _func();
    _cache[this] = (result == null) ? this : result;
    return result;
  }
}


defaultFunc() {
  print("Default Function Called");
  return 42;
}
main([args, function = const Lazy(defaultFunc)]) {
  print(function());
  print(function());
}

在 DartPad 中尝试

更新

可重复使用的Lazy&lt;T&gt; 在 Dart 中可能如下所示,但它也不适用于 const,并且如果计算需要引用实例成员 (this.xxx),则不能在字段初始化器中使用。

void main() {
  var sc = new SomeClass();
  print('new');
  print(sc.v);
}

class SomeClass {
  var _v  = new Lazy<int>(() {
    print('x');
    return 10;
  });
  int get v => _v();
}

class Lazy<T> {
  final Function _func;
  bool _isEvaluated = false;
  Lazy(this._func);
  T _value;
  T call() {
    if(!_isEvaluated) {
      if(_func != null) {
        _value = _func();
      }
      _isEvaluated = true;
    }
    return _value;
  }
}

DartPad试试吧

原创

http://matt.might.net/articles/implementing-laziness/ 的 Dart 版本使用闭包进行惰性求值:

void main() {
  var x = () { 
    print ("foo"); 
    return 10; 
  }();
  print("bar");
  print(x);
  // will print foo, then bar then 10.
  print('===');
  // But, the following Scala program:
  x = () { 
    print("foo"); 
    return 10; 
  };
  print ("bar");
  print (x());
  // will print bar, then foo, then 10, since it delays the computation of x until it’s actually needed.
}

DartPad试试吧

【讨论】:

  • 感谢您的回答。我熟悉你链接的这篇文章。这实际上是我在 Dart 中寻找良好的 惰性评估 实现的原因之一。您提供的两个示例对于我想要实现的目标都有缺陷。无论是否使用结果,都会计算第一个。 Second 不能与 Dart 中的类成员和常量构造函数一起使用。你能看看我的问题的编辑吗?
  • 我对它进行了更多研究。代码见DartPad。我认为没有办法在 Dart 中将惰性求值与 const 结合使用。
  • 静态成员在 Dart 中是惰性初始化的,但我想这也只能满足您的要求。
  • 有一种方法可以同时获得惰性缓存评估和常量:使用全局 Expando 来保存缓存值,而不是将它们放在对象上。见:dartpad.dartlang.org/8a668690499074a1b62f
【解决方案2】:

2021 年更新

延迟初始化现在是 2.12 版 dart 的一部分。

只需在变量声明中添加late 修饰符

late MyClass obj = MyClass();

并且这个对象只有在第一次使用时才会被初始化。

来自docs

Dart 2.12 添加了late 修饰符,它有两个用例:

  1. 声明一个不可为空的变量,该变量在其后初始化 声明。
  2. 延迟初始化变量。

在此处查看示例: https://dartpad.dev/?id=50f143391193a2d0b8dc74a5b85e79e3&null_safety=true

class A {
  String text = "Hello";
  
  A() {
    print("Lazily initialized");
  }
  
  sayHello() {
    print(text);
  }
}

class Runner {
  late A a = A();
  run() async {
    await Future.delayed(Duration(seconds: 3));
    a.sayHello();
  }
}

这里A类只有在使用时才会被初始化。

【讨论】:

    【解决方案3】:

    更新

    int _val;
    int get val => _val ??= 9;
    

    感谢@夜景

    我认为这个小sn-p可能会对你有所帮助......

    int _val;
    int get val => _val ?? _val = 9;
    

    【讨论】:

    • 或者更好dart int _val; int get val =&gt; _val ??= 9;
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-29
    • 1970-01-01
    • 2011-02-23
    相关资源
    最近更新 更多