5

我期待下面的代码打印chr7

import strutils

var splitLine = "chr7    127471196  127472363  Pos1  0  +".split()
var chrom, startPos, endPos = splitLine[0..2]
echo chrom

相反,它打印@[chr7, 127471196, 127472363].

有没有办法同时从序列中解压缩多个值?

如果元素不连续,那么执行上述操作的最简单方法是什么?例如:

var chrom, startPos, strand = splitLine[0..1, 5]

给出错误:

read_bed.nim(8, 40) Error: type mismatch: got (seq[string], Slice[system.int], int literal(5))
but expected one of:
system.[](a: array[Idx, T], x: Slice[system.int])
system.[](s: string, x: Slice[system.int])
system.[](a: array[Idx, T], x: Slice[[].Idx])
system.[](s: seq[T], x: Slice[system.int])

  var chrom, startPos, strand = splitLine[0..1, 5]
                                         ^
4

4 回答 4

5

这可以使用宏来完成。

import macros

macro `..=`*(lhs: untyped, rhs: tuple|seq|array): auto =
  # Check that the lhs is a tuple of identifiers.
  expectKind(lhs, nnkPar)
  for i in 0..len(lhs)-1:
    expectKind(lhs[i], nnkIdent)
  # Result is a statement list starting with an
  # assignment to a tmp variable of rhs.
  let t = genSym()
  result = newStmtList(quote do:
    let `t` = `rhs`)
  # assign each component to the corresponding
  # variable.
  for i in 0..len(lhs)-1:
    let v = lhs[i]
    # skip assignments to _.
    if $v.toStrLit != "_":
      result.add(quote do:
        `v` = `t`[`i`])

macro headAux(count: int, rhs: seq|array|tuple): auto =
  let t = genSym()
  result = quote do:
    let `t` = `rhs`
    ()
  for i in 0..count.intVal-1:
    result[1].add(quote do:
      `t`[`i`])

template head*(count: static[int], rhs: untyped): auto =
  # We need to redirect this through a template because
  # of a bug in the current Nim compiler when using
  # static[int] with macros.
  headAux(count, rhs)

var x, y: int
(x, y) ..= (1, 2)
echo x, y
(x, _) ..= (3, 4)
echo x, y
(x, y) ..= @[4, 5, 6]
echo x, y
let z = head(2, @[4, 5, 6])
echo z
(x, y) ..= head(2, @[7, 8, 9])
echo x, y

..=宏解包元组或序列分配。例如,您可以使用 来完成相同的操作var (x, y) = (1, 2),但也..=适用于 seqs 和数组,并允许您重用变量。

模板/宏从元组、数组或序列中head提取第一个count元素并将它们作为元组返回(然后可以像任何其他元组一样使用它,例如用letor解构var)。

于 2015-08-12T21:29:49.537 回答
3

对于正在寻找快速解决方案的任何人,这是我编写的一个名为unpack的灵活包。

您可以使用如下语法进行序列和对象解构/解包:

someSeqOrTupleOrArray.lunpack(a, b, c)
[a2, b2, c2] <- someSeqOrTupleOrArray

{name, job} <- tim

tom.lunpack(job, otherName = name)
{job, name: yetAnotherName} <- john
于 2018-12-05T06:30:20.003 回答
2

目前 Nim 中的模式匹配仅适用于tuples. 这也是有道理的,因为模式匹配需要静态已知的数量。例如,如果 的seq长度不是三,那么在您的示例中应该发生什么?请注意,在您的示例中,序列的长度只能在运行时确定,因此编译器不知道是否真的可以提取三个变量。

因此,我认为@def- 链接的解决方案正朝着正确的方向发展。此示例使用数组,它们确实具有静态已知的大小。在这种情况下,编译器知道元组的数量,即提取是明确定义的。

如果您想要一种替代方法(可能方便但不安全),您可以执行以下操作:

import macros

macro extract(args: varargs[untyped]): typed =
  ## assumes that the first expression is an expression
  ## which can take a bracket expression. Let's call it
  ## `arr`. The generated AST will then correspond to:
  ##
  ## let <second_arg> = arr[0]
  ## let <third_arg>  = arr[1]
  ## ...
  result = newStmtList()
  # the first vararg is the "array"
  let arr = args[0]
  var i = 0
  # all other varargs are now used as "injected" let bindings
  for arg in args.children:
    if i > 0:
      var rhs = newNimNode(nnkBracketExpr)
      rhs.add(arr)
      rhs.add(newIntLitNode(i-1))

      let assign = newLetStmt(arg, rhs) # could be replaced by newVarStmt
      result.add(assign)
    i += 1
  #echo result.treerepr


let s = @["X", "Y", "Z"]

s.extract(a, b, c)
# this essentially produces:
# let a = s[0]
# let b = s[1]
# let c = s[2]

# check if it works:
echo a, b, c

我还没有对长度进行检查seq,所以如果 seq 没有​​所需的长度,你只会得到越界错误。另一个警告:如果第一个表达式不是文字,则表达式将被评估/计算多次。

请注意,_在 let 绑定中允许使用文字作为占位符,这意味着您可以执行以下操作:

s.extract(a, b, _, _, _, x)

这将解决您的splitLine[0..1, 5]示例,顺便说一句,这根本不是有效的索引语法。

于 2015-08-12T11:05:58.483 回答
1

还有一个选择是package definesugar

import strutils, definesugar

# need to use splitWhitespace instead of split to prevent empty string elements in sequence
var splitLine = "chr7    127471196  127472363  Pos1  0  +".splitWhitespace()
echo splitLine

block:
  (chrom, startPos, endPos) := splitLine[0..2]
  echo chrom # chr7
  echo startPos # 127471196
  echo endPos # 127472363

block:
  (chrom, startPos, strand) := splitLine[0..1] & splitLine[5] # splitLine[0..1, 5] not supported
  echo chrom
  echo startPos
  echo strand # +

# alternative syntax
block:
  (chrom, startPos, *_, strand) := splitLine
  echo chrom
  echo startPos
  echo strand

有关最近的讨论,请参阅https://forum.nim-lang.org/t/7072

于 2020-11-12T09:06:10.027 回答