मैं एक 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) वांछित एपीआई

मेरा उपयोग मामला उससे अधिक जटिल है, लेकिन यह एक कमांड-लाइन प्रोग्राम के बराबर है जो तर्क के रूप में एक इनपुट टेक्स्ट, एक "बारकोड" और एक बारकोड प्रारंभ स्थिति लेता है, और यह बताता है कि बारकोड इनपुट टेक्स्ट में मौजूद है या नहीं। निर्दिष्ट स्थिति। यदि स्थिति को ऋणात्मक पूर्णांक के रूप में दिया जाता है, तो इसका अर्थ है कि हम अंत से एक स्थिति निर्दिष्ट करते हैं।

मेरे पास उम्मीद के मुताबिक कुछ काम कर रहा है:

$ 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 ऑब्जेक्ट को परिभाषित करना चाहता हूं जिसे मैं बार-बार कंप्यूटिंग के बजाय पुन: उपयोग कर सकता हूं टुकड़ा "इन-प्लेस"

1
bli 3 सितंबर 2020, 19:56

2 जवाब

सबसे बढ़िया उत्तर

सभी मुद्दे इस तथ्य से संबंधित हैं कि आपका प्रकार एक Type Class< है /ए>. यह एक छद्म प्रकार है जिसका उपयोग केवल संकलन समय पर proc अधिभार (या is ऑपरेटर के लिए) के पैरामीटर के रूप में किया जा सकता है। विशेष रूप से इसे var (आपके द्वारा रिपोर्ट की गई पहली त्रुटि) को असाइन नहीं किया जा सकता है और इसे रन टाइम पर गतिशील रूप से उपयोग नहीं किया जा सकता है।

आपको मिलने वाली अन्य 2 त्रुटियां 1 के कारण हैं) तथ्य यह है कि s1 अगर दायरे से बाहर परिभाषित नहीं है। 2) तथ्य यह है कि संकलक s1 के लिए एक अद्वितीय प्रकार चाहता है (यह पहले से प्रकार का अनुमान लगाता है और फिर अन्य खंड के लिए लागू होता है)।

ऑब्जेक्ट वेरिएंट (सम प्रकार, निम में बीजगणितीय डेटा प्रकार भी ; शब्दावली यूनियन प्रकार अक्सर निम में उपयोग नहीं किया जाता है) आमतौर पर गतिशील को लागू करने का सबसे सीधा तरीका है निम में प्रकार (क्लासिक उदाहरण JsonNode है)।

संपादित करें: वांछित एपीआई पर

चूंकि "स्लाइस" के पुन: उपयोग और प्रदर्शन में सुधार पर जोर दिया गया है, निम्नलिखित (यहां भी: 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_start और barcode को अलग-अलग ग्रंथों (संभवतः परिवर्तनीय लंबाई) के विरुद्ध कई बार मिलान करने की आवश्यकता है
  • एक स्ट्रिंग के "स्लाइस" की गणना करने से बचना बेहतर है, क्योंकि यह एक नई स्ट्रिंग आवंटित करता है (देखें यहां)। मुझे संदेह है कि यह स्टार्ट इंडेक्स के प्रीकंप्यूटेशन से बड़ा प्रदर्शन सुधार है।
  • पिछले दो बिंदुओं से, मैच को कई बार लागू करने से पहले "संकलित" होने वाली वस्तु वास्तव में एक स्लाइस नहीं है (इसलिए नाम myPattern)
3
pietroppeter 9 सितंबर 2020, 18:12

इजहार

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

संपादित करें आप एक स्लाइस को पास करने में सक्षम होना चाहते थे जिसका उपयोग विभिन्न इनपुट स्ट्रिंग्स पर किया जा सकता था। यहां बताया गया है कि यह उपरोक्त के साथ कैसा दिखेगा:

#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"
3
shirleyquirk 9 सितंबर 2020, 18:23