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"