微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

尼姆:如何动态定义可以向前或向后的切片? 编辑09/09/2020所需的API

如何解决尼姆:如何动态定义可以向前或向后的切片? 编辑09/09/2020所需的API

我想动态定义一个Slice,它可以基于前向索引或后向索引(取决于其起始位置是正数还是负数)。

我正在尝试https://play.nim-lang.org/

我尝试了以下联合类型:

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(2,5) Error: invalid type: 'mySlice' for var

我尝试过

let s = "1234567890"
let bcStart = 3
let bcLen = 3
if bcLen < 0:
  let sl = (bcStart-1)..<(bcStart+bcLen-1)
else:
  let 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]

还有另一种失败的方式:

/usercode/in.nim(8,23) Error: type mismatch: got <HSlice[system.BackwardsIndex,system.BackwardsIndex]> but expected 'HSlice[system.int,system.int]'

为什么会失败,我该怎么办?

编辑(09/09/2020)所需的API

我的用例比这要复杂得多,但是它相当于一个命令行程序,该程序以输入文本,“条形码”和条形码的起始位置为参数,并告诉输入中是否存在条形码。文字在指定位置。如果位置为负整数,则表示我们从末尾开始指定位置。

我的工作正常:

$ cat src/test.nim
import docopt
from strutils import parseInt

# https://github.com/docopt/docopt.nim
const doc = """

Usage:
  test -t <input_text> -b <barcode> -s <barcode_start>

-h --help                                 Show this help message and exit.
-t --input_text <input_text>              Text in which to search for the barcode.
-b --barcode <barcode>                    Barcode to search.
-s --barcode_start <barcode_start>        Position at which the barcode starts (1-based),negative if from end.
"""

proc match_text(inText: string,barcode: string,bcStart: int): bool =
  var
    bcSeq: string
    bcLen: int = barcode.len
  if bcStart < 0:
    bcSeq = inText[^(bcLen - bcStart - 1)..^(-bcStart)]
  else:
    bcSeq = inText[(bcStart-1)..<(bcStart + bcLen - 1)]
  if bcSeq == barcode:
    result = true
  else:
    result = false

when isMainModule:
  let args = docopt(doc)
  var
    barcode: string
    inText: string
    bcStart: int
  for opt,val in args.pairs():
    case opt
    of "-t","--input_text":
      inText = $args[opt]
    of "-b","--barcode":
      barcode = $args[opt]
    of "-s","--barcode_start":
      bcStart = parseInt($val)
    else:
      echo "UnkNown option" & opt
      quit(QuitFailure)
  if match_text(inText,barcode,bcStart):
    echo "Matches"
  else:
    echo "Doesn't match"

建筑工程:

$ 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

但是,在我的实际应用程序中,我在不同的文本段中多次使用了相同的切片,因此我想定义一个Slice对象,我可以将其重复使用重复计算“就地”切片的过程

解决方法

所有问题均与您的类型为Type Class的事实有关。这是伪类型,只能在编译时用作proc重载(或is运算符)的参数。特别是,它不能分配给var(您报告的第一个错误),并且不能在运行时动态使用。

您收到的其他2个错误是由于1)s1没有在if范围之外定义的事实。 2)编译器希望为s1使用唯一类型的事实(它首先从if推断类型,然后为else子句强制执行)。

Object variants(也是Sum类型,Nim中的代数数据类型;术语Union Type在Nim中不经常使用)通常是在Nim中实现动态类型的最直接方法(经典示例是JsonNode)

编辑:在所需的API上

由于重点在于“ Slice”的可重用性和性能改进,因此可以使用以下内容(也在此处:https://play.nim-lang.org/#ix=2wXp):

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"

备注:

  • 我认为固定的barcode_startbarcode需要针对不同的文本(可能是可变长度)进行多次匹配
  • 最好避免计算字符串的“切片”,因为它会分配新的字符串(请参见here)。我怀疑这比启动索引的预计算有更大的性能改进。
  • 根据前两点,在多次应用匹配之前要“编译”的对象实际上不是切片(因此名称为myPattern)
,

表达式

let sl = if (bcLen >0): bcLen else: BackwardsIndex(bcLen)#Error: type mismatch!

无法以静态类型的语言进行编译,因此您需要使用继承或Variant框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] #  ""

修改 您希望能够传递可以在不同输入字符串上使用的Slice。这就是上面的样子:

#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"

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。