Types 尼姆:如何动态地定义一个可以向前或向后的切片?
我想动态定义一个Types 尼姆:如何动态地定义一个可以向前或向后的切片?,types,slice,nim-lang,Types,Slice,Nim Lang,我想动态定义一个切片,它可以基于向前或向后索引(取决于它的起始位置是正数还是负数) 我正在试穿 我尝试了一种联合类型,如下所示: type mySlice = Slice[BackwardsIndex] | Slice[int] var sl: mySlice let s = "1234567890" let bcStart = 3 let bcLen = 3 if bcLen < 0: sl = (bcStart-1)..<(bcStart+bcLen-1)
切片
,它可以基于向前或向后索引(取决于它的起始位置是正数还是负数)
我正在试穿
我尝试了一种联合类型,如下所示:
type mySlice = Slice[BackwardsIndex] | Slice[int]
var sl: mySlice
let s = "1234567890"
let bcStart = 3
let bcLen = 3
if bcLen < 0:
sl = (bcStart-1)..<(bcStart+bcLen-1)
else:
sl = ^(bcStart+bcLen-1)..^(bcStart)
echo s[sl]
/usercode/in.nim(5, 7) Hint: 'sl' is declared but not used [XDeclaredButNotUsed]
/usercode/in.nim(7, 7) Hint: 'sl' is declared but not used [XDeclaredButNotUsed]
/usercode/in.nim(8, 8) Error: undeclared identifier: 'sl'
而这失败的原因如下:
type mySlice = Slice[BackwardsIndex] | Slice[int]
var sl: mySlice
let s = "1234567890"
let bcStart = 3
let bcLen = 3
if bcLen < 0:
sl = (bcStart-1)..<(bcStart+bcLen-1)
else:
sl = ^(bcStart+bcLen-1)..^(bcStart)
echo s[sl]
/usercode/in.nim(5, 7) Hint: 'sl' is declared but not used [XDeclaredButNotUsed]
/usercode/in.nim(7, 7) Hint: 'sl' is declared but not used [XDeclaredButNotUsed]
/usercode/in.nim(8, 8) Error: undeclared identifier: 'sl'
我还尝试了以下方法:
let s = "1234567890"
let bcStart = 3
let bcLen = 3
let sl =
if bcLen < 0:
(bcStart-1)..<(bcStart+bcLen-1)
else:
^(bcStart+bcLen-1)..^(bcStart)
echo s[sl]
测试工作:
$ nimble build
# [successful build output]
$ ./bin/test -t aacgttb -b aa -s 1
Matches
$ ./bin/test -t aacgttb -b aa -s 2
Doesn't match
$ ./bin/test -t aacgttb -b tt -s -1
Doesn't match
$ ./bin/test -t aacgttb -b tt -s -2
Matches
然而,在我的实际应用程序中,我在不同的文本片段中多次使用相同的切片,因此我想定义一个可以重复使用的
切片
对象,而不是重复“就地”计算切片这些问题都与您的类型是。这是一个伪类型,只能在编译时用作proc重载(或is
运算符)的参数。特别是,它不能分配给var
(您报告的第一个错误),并且不能在运行时动态使用
其他2个错误是由于1)未在if范围外定义s1
。2) 编译器希望s1
具有唯一的类型(它从第一个if推断类型,然后为else子句强制)
(也是Nim中的和类型、代数数据类型;Nim中不经常使用术语)通常是Nim中实现动态类型的最直接的方法(典型示例是JsonNode)
编辑:在所需的API上
由于重点是“切片”的可重用性和性能改进,因此可以使用以下内容(此处也有:):
type myPattern = object
barcode: string
start: int
isBackwards: bool
proc initMyPattern(barcode: string, bcStart: int): myPattern =
# no need to have a new variable for barcode.len since it is already available (not computed) for a string
# also no need to precompute last index of slice because it will not be used
if bcStart < 0:
myPattern(barcode: barcode, start: barcode.len - bcStart - 1, isBackwards: true)
else:
myPattern(barcode: barcode, start: bcStart - 1, isBackwards: false)
proc startIndex(inText: string, p: myPattern): int =
if p.isBackwards:
# this cannot be precomputed if len of inText is variable
inText.len - p.start
else:
p.start
proc match(inText: string, p: myPattern): bool =
var
i = startIndex(inText, p)
j = 0
# case where inText is not long enough to match
if i + p.barcode.len - 1 >= inText.len:
return false
# instead of computing the slice of inText (which allocates a new string), we directly iterate over indices
while j < p.barcode.len:
if p.barcode[j] != inText[i]:
return false
inc i
inc j
return true
assert "aacgttb".match initMyPattern("aa", 1)
assert not "aacgttb".match initMyPattern("aa", 2)
assert not "aacgttb".match initMyPattern("tt", -1)
assert "aacgttb".match initMyPattern("tt", -2)
assert not "aacgttb".match initMyPattern("ttbb", -2)
echo "tests successful"
type myPattern=object
条形码:字符串
起始:int
伊斯:布尔
proc initMyPattern(条形码:string,bcStart:int):myPattern=
#barcode.len不需要新变量,因为它已可用于(未计算)字符串
#也不需要预先计算切片的最后一个索引,因为它不会被使用
如果bcStart<0:
myPattern(条形码:条形码,开始:barcode.len-bcStart-1,isBackwards:true)
其他:
myPattern(条形码:条形码,开始:bcStart-1,isBackwards:false)
proc startIndex(inText:string,p:myPattern):int=
如果p.isBackwards:
#如果inText的len为变量,则无法预计算此值
inText.len-p.start
其他:
p、 开始
过程匹配(inText:string,p:myPattern):bool=
变量
i=起始索引(inText,p)
j=0
#inText长度不足以匹配的情况
如果i+p.barcode.len-1>=inText.len:
返回错误
#我们直接迭代索引,而不是计算inText的切片(它分配一个新字符串)
而j
备注:
- 我假设固定的
和barcode\u start
需要针对不同的文本(可能是可变长度)进行多次匹配barcode
- 最好避免计算字符串的“切片”,因为它会分配一个新字符串(请参阅)。我怀疑这比开始索引的预计算有更大的性能改进
- 根据前面两点,在多次应用match之前要“编译”的对象实际上不是一个切片(因此名为myPattern)
- 表达式
let sl = if (bcLen >0): bcLen else: BackwardsIndex(bcLen)#Error: type mismatch!
无法在静态类型语言中编译,因此需要使用继承或变量框sl
然后在生成切片时再次取消装箱。您可以这样做:
type
PosOrNegKind = enum
Pos,Neg
PosOrNeg = object
case kind:PosOrNegKind
of Pos: posVal:int
of Neg: negVal:int
mySlice = object
beg,fin:PosOrNeg
proc `[]`(str:string,sl:mySlice):string =
let beg = case sl.beg.kind
of Pos: sl.beg.posVal
of Neg: len(str) + sl.beg.negVal
let fin = case sl.fin.kind
of Pos: sl.fin.posVal
of Neg: len(str) + sl.fin.negVal
str[beg .. fin]
proc posOrNeg(x:int):PosOrNeg =
if (x >= 0): PosOrNeg(kind: Pos, posVal: x)
else: PosOrNeg(kind: Neg, negVal: x)
proc createSlice(beg,fin:int):mySlice =
result.beg = posOrNeg(beg)
result.fin = posOrNeg(fin)
let sl = createSlice(3,-3)
echo s[sl]# "34567"
但是对于这个特定的用例,您在值本身中有一个自然的鉴别器(无论int是正的还是负的),因此您可以执行以下操作:
type
MySlice = object
a,b:int
proc `--`(a,b:int):MySlice = MySlice(a: a, b: b)
proc `[]`(s:string,m:MySlice):string =
var beg = if (m.a < 0): s.len + m.a else: m.a
var fin = if (m.b < 0): s.len + m.b else: m.b
#safety checks
if fin < beg: return ""
if fin >= s.len: fin = s.len - 1
if beg < 0: beg = 0
s[beg..fin]
echo s[3 -- 5] # "345"
echo s[3 -- -2] # "345678"
echo s[-5 -- 9] # "56789"
echo s[-8 -- -2] # "2345678"
echo s[-1 -- 1] # ""
类型
MySlice=对象
a、 b:int
proc`-`(a,b:int):MySlice=MySlice(a:a,b:b)
proc`[]`(s:string,m:MySlice):string=
var beg=如果(m.a<0):s.len+m.a其他:m.a
var fin=if(m.b<0):s.len+m.b其他:m.b
#安全检查
如果fin=s.len:fin=s.len-1
如果beg<0:beg=0
s[乞求..鳍]
回声s[3--5]#“345”
回声s[3--2]#“345678”
回声s[-5--9]#“56789”
回声s[-8---2]#“2345678”
回声s[-1--1]#“
编辑
您希望能够传递可用于不同输入字符串的片段。以下是使用上述选项时的效果:
#fixing off-by-one errors left as an exercise for the reader
proc make_slice(barcode:string,bcStart:int):mySlice=
let bcLen = barcode.len
if bcStart < 0:
(bcStart - bcLen) -- bcStart
else:
bcStart -- (bcStart + bcLen)
let sl = make_slice("abaca", -3)
for inText in @["abacus","abacadacaba","abracadabra"]:
if inText[sl] == barcode:
echo "matches"
#将作为练习留给读者的错误逐一修复
proc make_slice(条形码:string,bcStart:int):mySlice=
设bcLen=barcode.len
如果bcStart<0:
(bcStart-bcLen)--bcStart
其他:
bcStart--(bcStart+bcLen)
设sl=制作_切片(“abaca”,-3)
对于@[“算盘”、“算盘”、“abacadacaba”、“abracadabra”]中的inText:
如果inText[sl]==条形码:
回声“匹配”
你能给出你想要实现的API的例子吗(我不清楚发布的例子)?@pietroppeter很抱歉我反应迟钝。我添加了一个我想实现的示例API(更确切地说:我实际实现了,但我想“优化”内部实现)?让mySlice=(bcStart-1)…我认为这里不能使用Slice[T]泛型类型(或HSlide[T,U]),因为在编译时,他不知道T是int还是backardsindex。下面我更新了我的答案,认为应该避免以切片的方式思考。如果最后一行是API OP想要实现的类型,我看不出与标准语法相比有什么优势(例如,echo[^5..9]
,除了最后一行之外,其他所有行都可以编译)。不过答案很好:)哦,我不喜欢它的用途。但希望这表明在Nim中添加所需的语法是非常优雅的。感谢您提供的有用答案。实际上,我会说YXZ问题:我的simplifi
#fixing off-by-one errors left as an exercise for the reader
proc make_slice(barcode:string,bcStart:int):mySlice=
let bcLen = barcode.len
if bcStart < 0:
(bcStart - bcLen) -- bcStart
else:
bcStart -- (bcStart + bcLen)
let sl = make_slice("abaca", -3)
for inText in @["abacus","abacadacaba","abracadabra"]:
if inText[sl] == barcode:
echo "matches"