从序列中解压多个变量
Unpack multiple variables from sequence
我希望下面的代码能够打印 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]
^
目前 Nim 中的模式匹配仅适用于 tuples
。这也是有道理的,因为模式匹配需要一个静态已知的元数。例如,如果 seq
的长度不是三,在您的示例中应该发生什么?请注意,在您的示例中,序列的长度只能在运行时确定,因此编译器不知道是否真的可以提取三个变量。
因此我认为由@def- 链接的 the solution 正朝着正确的方向前进。此示例使用数组,do 具有静态已知大小。在这种情况下,编译器知道元组元数,即提取定义明确。
如果您想要一种替代方法(可能方便但不安全),您可以这样做:
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 没有所需的长度,你只会得到越界错误。另一个警告:如果第一个表达式不是文字,表达式将是 evaluated/calculated 几次。
请注意,_
文字允许在 let 绑定中作为占位符,这意味着您可以这样做:
s.extract(a, b, _, _, _, x)
这将解决您的 splitLine[0..1, 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)
完成相同的操作,但 ..=
也适用于序列和数组,并允许您重用变量。
head
template/macro 从元组、数组或序列中提取第一个 count
元素,并将它们 returns 作为元组(然后可以像任何其他元组,例如使用 let
或 var
).
进行解构
对于任何正在寻找快速解决方案的人来说,这是我编写的一个名为 unpack 的灵活包。
您可以使用如下语法来执行序列和对象 destructuring/unpacking:
someSeqOrTupleOrArray.lunpack(a, b, c)
[a2, b2, c2] <- someSeqOrTupleOrArray
{name, job} <- tim
tom.lunpack(job, otherName = name)
{job, name: yetAnotherName} <- john
另一个选项是 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
我希望下面的代码能够打印 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]
^
目前 Nim 中的模式匹配仅适用于 tuples
。这也是有道理的,因为模式匹配需要一个静态已知的元数。例如,如果 seq
的长度不是三,在您的示例中应该发生什么?请注意,在您的示例中,序列的长度只能在运行时确定,因此编译器不知道是否真的可以提取三个变量。
因此我认为由@def- 链接的 the solution 正朝着正确的方向前进。此示例使用数组,do 具有静态已知大小。在这种情况下,编译器知道元组元数,即提取定义明确。
如果您想要一种替代方法(可能方便但不安全),您可以这样做:
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 没有所需的长度,你只会得到越界错误。另一个警告:如果第一个表达式不是文字,表达式将是 evaluated/calculated 几次。
请注意,_
文字允许在 let 绑定中作为占位符,这意味着您可以这样做:
s.extract(a, b, _, _, _, x)
这将解决您的 splitLine[0..1, 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)
完成相同的操作,但 ..=
也适用于序列和数组,并允许您重用变量。
head
template/macro 从元组、数组或序列中提取第一个 count
元素,并将它们 returns 作为元组(然后可以像任何其他元组,例如使用 let
或 var
).
对于任何正在寻找快速解决方案的人来说,这是我编写的一个名为 unpack 的灵活包。
您可以使用如下语法来执行序列和对象 destructuring/unpacking:
someSeqOrTupleOrArray.lunpack(a, b, c)
[a2, b2, c2] <- someSeqOrTupleOrArray
{name, job} <- tim
tom.lunpack(job, otherName = name)
{job, name: yetAnotherName} <- john
另一个选项是 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