如何在 Nim 中编写宏 list.findBy(key, value)?

How to write macro list.findBy(key, value) in Nim?

是否可以编写 list.findBy(key, value) 宏,以便:

let people = @[(name: "John"), (name: "Sarah")]
echo people.findBy("name", "John")

理想情况下,它应该在编译时验证“名称”。

我尝试了一些代码,但它不起作用,play:

import macros, options

macro findBy*[T](list: seq[T], field: string, value: untyped): Option[T] =
  quote do:
    for v in list:
      if v.`field` == value: return

let people = @[(name: "John"), (name: "Sarah")]
echo people.findBy("name", "John")

Here 是一个正常运行的实现。

import macros, options, typetraits

macro findBy*[T](list: seq[T], field: untyped, value: untyped): untyped =
  quote do:
    var res = none(`list`.elementType)
    for v in `list`:
      if v.`field` == `value`:
        res = some(v)
        break
    res

let people = @[(name: "John"), (name: "Sarah")]
echo people.findBy(name, "John")


您可以使用@Jason 的实现,尽管在这种特殊情况下 tempate 就足够了(没有必要使用 quote do

import std/[macros, options]

template findBy*[T](list: seq[T], field: untyped, value: untyped): Option[T] =
  var res: Option[T] # Create result variable
  for v in `list`: # Splice all parameters into loop
    if v.`field` == `value`: 
      res = some(v)
    
  res # 'return' from template is a final expression

let people = @[(name: "John"), (name: "Sarah")]

expandMacros:
  echo people.findBy(name, "John")
                   # ^ Note that `name` is passed as identifier, not string

这将打印

Some((name: "John"))

findBy 的扩展代码如下所示:


echo [
  var res`gensym0: Option[T]
  for v`gensym0 in items(people):
    if v`gensym0.name == "John":
      res`gensym0 = some(v`gensym0)
  res`gensym0]