একটি স্ট্রিংয়ের রেখার উপরে আইট্রেট করুন


119

আমার এইরকম সংজ্ঞা দেওয়া হয়েছে একটি বহু-লাইন স্ট্রিং:

foo = """
this is 
a multi-line string.
"""

এই স্ট্রিংটি আমি লিখছি এমন একটি পার্সারের পরীক্ষার ইনপুট হিসাবে ব্যবহার করেছি। পার্সার-ফাংশন fileইনপুট হিসাবে একটি- অবজেক্ট গ্রহণ করে এবং এটির উপরে পুনরাবৃত্তি করে। এটি next()লাইনগুলি এড়িয়ে যাওয়ার জন্য সরাসরি পদ্ধতিটিকে কল করে না , তাই আমার সত্যিকার অর্থে ইনপুট হিসাবে একটি পুনরুক্তি প্রয়োজন, পুনরাবৃত্তিযোগ্য নয়। আমার একটি পুনরুক্তি দরকার যা সেই স্ট্রিংয়ের স্বতন্ত্র রেখাগুলির উপরে পুনরাবৃত্তি করে যেমন-- fileবজেক্ট কোনও পাঠ্য-ফাইলের লাইনের উপর দিয়ে যায়। আমি অবশ্যই এটি এর মতো করতে পারি:

lineiterator = iter(foo.splitlines())

এটি করার আরও প্রত্যক্ষ উপায় আছে কি? এই দৃশ্যে স্ট্রিংটি বিভাজনের জন্য একবার ট্র্যাভার করতে হয়েছে এবং তারপরে আবার পার্সার দিয়ে যেতে হবে। আমার পরীক্ষার ক্ষেত্রে এটি কোনও ব্যাপার নয়, যেহেতু সেখানে স্ট্রিং খুব ছোট, আমি কেবল কৌতূহল থেকে জিজ্ঞাসা করছি। পাইথনের এ জাতীয় স্টাফগুলির জন্য অনেকগুলি দরকারী এবং দক্ষ বিল্ট-ইন রয়েছে তবে আমি এই প্রয়োজন অনুসারে এমন কিছু পাই না।


12
আপনি কি জানেন যে আপনি foo.splitlines()ডানদিকে পুনরাবৃত্তি করতে পারেন ?
সাইলেন্টগোস্ট

"পার্সার দ্বারা আবার" বলতে কী বোঝ?
Danben

4
@ সাইলেন্টগোস্ট: আমি মনে করি বিন্দুটি স্ট্রিংটিকে দু'বার পুনরুক্ত করা না। একবার এটি দ্বারা পুনরাবৃত্তি করা হয় splitlines()এবং দ্বিতীয়বার এই পদ্ধতির ফলাফলকে পুনরাবৃত্তি করে।
ফেলিক্স ক্লিং

2
ডিফল্টরূপে স্প্লিটলাইন () কোনও পুনরুক্তি ফেরত দেয় না এমন কোনও কারণ আছে কি? আমি ভেবেছিলাম যে প্রবণতাটি সাধারণত পুনরাবৃত্তদের জন্য এটি করা ছিল। বা ডিক.কিজ () এর মতো নির্দিষ্ট ফাংশনগুলির জন্যই এটি সত্য?
কর্নো

উত্তর:


144

এখানে তিনটি সম্ভাবনা রয়েছে:

foo = """
this is 
a multi-line string.
"""

def f1(foo=foo): return iter(foo.splitlines())

def f2(foo=foo):
    retval = ''
    for char in foo:
        retval += char if not char == '\n' else ''
        if char == '\n':
            yield retval
            retval = ''
    if retval:
        yield retval

def f3(foo=foo):
    prevnl = -1
    while True:
      nextnl = foo.find('\n', prevnl + 1)
      if nextnl < 0: break
      yield foo[prevnl + 1:nextnl]
      prevnl = nextnl

if __name__ == '__main__':
  for f in f1, f2, f3:
    print list(f())

এটিকে মূল স্ক্রিপ্ট হিসাবে চালানো তিনটি ফাংশন সমতুল্য হওয়ার বিষয়টি নিশ্চিত করে। সঙ্গে timeit(এবং * 100জন্য fooআরও ভালো পরিমাপের জন্য সারগর্ভ স্ট্রিং পেতে):

$ python -mtimeit -s'import asp' 'list(asp.f3())'
1000 loops, best of 3: 370 usec per loop
$ python -mtimeit -s'import asp' 'list(asp.f2())'
1000 loops, best of 3: 1.36 msec per loop
$ python -mtimeit -s'import asp' 'list(asp.f1())'
10000 loops, best of 3: 61.5 usec per loop

দ্রষ্টব্য list(), কেবল নির্মিত না, পুনরাবৃত্তিগুলি ট্র্যাভারেজ করা হয়েছে তা নিশ্চিত করার জন্য আমাদের কল প্রয়োজন we

IOW, নিষ্পাপ বাস্তবায়ন এত তাড়াতাড়ি এটি মজাদারও নয়: findকলগুলির সাথে আমার প্রচেষ্টার চেয়ে 6 গুণ বেশি দ্রুতগতি হয় যা নিম্ন স্তরের পদ্ধতির চেয়ে 4 গুণ দ্রুত হয়।

রাখার পাঠ: পরিমাপ সর্বদা একটি ভাল জিনিস (তবে অবশ্যই সঠিক হতে হবে); স্ট্রিং পদ্ধতিগুলি splitlinesখুব দ্রুত উপায়ে প্রয়োগ করা হয়; খুব নিম্ন স্তরে প্রোগ্রামিংয়ের মাধ্যমে স্ট্রিংগুলি একসাথে স্থাপন করা (উদাহরণস্বরূপ লুপগুলি দ্বারা)+= , খুব ছোট টুকরোগুলির ) বেশ ধীর হতে পারে।

সম্পাদনা : যোগ করা @ জ্যাকবের প্রস্তাব, অন্যের মতো একই ফলাফল দেওয়ার জন্য কিছুটা সংশোধন করা (একটি লাইনে ফাঁকা ফাঁকা রাখা হয়), অর্থাত:

from cStringIO import StringIO

def f4(foo=foo):
    stri = StringIO(foo)
    while True:
        nl = stri.readline()
        if nl != '':
            yield nl.strip('\n')
        else:
            raise StopIteration

পরিমাপ দেয়:

$ python -mtimeit -s'import asp' 'list(asp.f4())'
1000 loops, best of 3: 406 usec per loop

.findভিত্তিক পদ্ধতির মতো ততটা ভাল নয় - তবুও, মাথায় রাখার মতো কারণ এটি ছোট অফ-ওয়ান বাগের চেয়ে কম ঝুঁকিপূর্ণ হতে পারে (আমার f3উপরের মতো +1 এবং -1 এর উপস্থিতিগুলি দেখতে কোনও লুপটি স্বয়ংক্রিয়ভাবে হওয়া উচিত) একের পর এক সন্দেহের সূত্রপাত - এবং এমন অনেক লুপের উচিত যা এই জাতীয় টুইটগুলির অভাব রয়েছে এবং সেগুলি থাকা উচিত - যদিও আমি বিশ্বাস করি যে আমার কোডটিও ঠিক আছে যেহেতু আমি অন্যান্য ফাংশনগুলির সাথে এর ফলাফল আউট পরীক্ষা করতে সক্ষম হয়েছি)।

তবে বিভক্ত-ভিত্তিক পদ্ধতির এখনও নিয়ম রয়েছে।

একদিকে: সম্ভবত এর চেয়ে ভাল স্টাইলটি হ'ল f4:

from cStringIO import StringIO

def f4(foo=foo):
    stri = StringIO(foo)
    while True:
        nl = stri.readline()
        if nl == '': break
        yield nl.strip('\n')

কমপক্ষে, এটি কিছুটা কম ভার্বোস। Trailing স্ট্রিপ প্রয়োজন \nগুলি দুর্ভাগ্যবশত এর পরিষ্কার এবং দ্রুত প্রতিস্থাপন নিষিদ্ধ whileসঙ্গে লুপ return iter(stri)( iterঅংশ কিসের পাইথন আধুনিক সংস্করণে অপ্রয়োজনীয় হয়, আমি বিশ্বাস করি 2.3 বা 2.4 থেকে, কিন্তু এটি নির্দোষ এর)। চেষ্টা করার মতোও হতে পারে:

    return itertools.imap(lambda s: s.strip('\n'), stri)

বা এর বৈচিত্রগুলি - তবে আমি এখানে থেমে যাচ্ছি কারণ এটি একটি তাত্ত্বিক অনুশীলনের stripউপর ভিত্তি করে, সবচেয়ে সহজ এবং দ্রুততম, সবচেয়ে কার্যকর t


এছাড়াও, (line[:-1] for line in cStringIO.StringIO(foo))বেশ দ্রুত; প্রায় নিষ্পাপ বাস্তবায়ন হিসাবে দ্রুত, কিন্তু বেশ না।
ম্যাট অ্যান্ডারসন

এই দুর্দান্ত উত্তরের জন্য আপনাকে ধন্যবাদ। আমি অনুমান করি যে এখানে মূল পাঠটি (যেমন আমি অজগর থেকে নতুন) এটি timeitএকটি অভ্যাস ব্যবহার করা।
বিজন পোলাক্স

@ স্পেস, হ্যাঁ, সময় নির্ধারিত সময়টি ভাল, আপনি যে কোনও সময় পারফরম্যান্সের বিষয়ে যত্নশীল হন (এটি সাবধানতার সাথে ব্যবহার করতে ভুলবেন না, যেমন listএক্ষেত্রে প্রকৃত সমস্ত প্রাসঙ্গিক সময়কে কল করার জন্য আমার নোটটি দেখুন !))।
অ্যালেক্স মার্টেলি

6
মেমরির খরচ কি? split()তালিকার কাঠামোগুলি ছাড়াও সমস্ত বিভাগের একটি অনুলিপি ধারণ করে স্পষ্টভাবে পারফরম্যান্সের জন্য মেমরির ব্যবসা করে।
ivan_pozdeev

3
আমি প্রথমে আপনার মন্তব্যে সত্যিই বিভ্রান্ত হয়ে পড়েছিলাম কারণ আপনি সময় প্রয়োগের ফলাফলগুলি তাদের প্রয়োগ এবং সংখ্যার বিপরীত ক্রমে তালিকাভুক্ত করেছিলেন। = পি
জেমসডলিন

53

আপনি "তারপরে আবার বিশ্লেষক দ্বারা" অর্থ কী তা আমি নিশ্চিত নই। বিভক্ত হয়ে যাওয়ার পরে, স্ট্রিংটির আর কোনও ট্র্যাভারসাল নেই , কেবল বিভক্ত স্ট্রিংগুলির তালিকার একটি ট্র্যাভারসাল । এটি সম্ভবত এটি সম্পাদন করার দ্রুততম উপায় হবে, যতক্ষণ না আপনার স্ট্রিংয়ের আকার একেবারে বিশাল নয়। অজগরটি অপরিবর্তনীয় স্ট্রিংগুলি ব্যবহার করে এমন অর্থ আপনার অবশ্যই হওয়া উচিত সর্বদা একটি নতুন স্ট্রিং তৈরি , সুতরাং এটি কোনও কোনও মুহুর্তে সম্পন্ন করতে হবে।

যদি আপনার স্ট্রিংটি খুব বড় হয় তবে অসুবিধাটি মেমোরির ব্যবহারে রয়েছে: আপনার কাছে একই সাথে মেমরির মূল স্ট্রিং এবং স্প্লিট স্ট্রিংগুলির একটি তালিকা থাকবে, যা প্রয়োজনীয় মেমরিটিকে দ্বিগুণ করবে। একটি পুনরুক্তি পদ্ধতির প্রয়োজন অনুসারে একটি স্ট্রিং তৈরি করে এটি আপনাকে সংরক্ষণ করতে পারে, যদিও এটি "বিভাজন" জরিমানা প্রদান করে। তবে, আপনার স্ট্রিংটি যদি বড় হয় তবে আপনি সাধারণত আনস্লিট স্ট্রিং মেমরির মধ্যে এড়াতে চান । কেবল কোনও ফাইল থেকে স্ট্রিং পড়লে ভাল হবে, যা ইতিমধ্যে আপনাকে লাইন হিসাবে পুনরুক্ত করার অনুমতি দেয়।

তবে আপনার যদি ইতিমধ্যে স্মৃতিতে একটি বিশাল স্ট্রিং থাকে, তবে একটি পদ্ধতির মধ্যে স্ট্রিংআইও ব্যবহার করা হবে, যা একটি স্ট্রিংয়ের সাথে ফাইলের মতো ইন্টারফেস উপস্থাপন করে, লাইন দ্বারা পুনরাবৃত্তি করার অনুমতি দেওয়ার সাথে (পরবর্তীভাবে নতুন লাইনের সন্ধানের জন্য অভ্যন্তরীণভাবে .find ব্যবহার করে)। তারপরে আপনি পাবেন:

import StringIO
s = StringIO.StringIO(myString)
for line in s:
    do_something_with(line)

5
দ্রষ্টব্য: পাইথন 3 এর জন্য আপনাকে এর ioজন্য প্যাকেজটি ব্যবহার করতে io.StringIOহবে , উদাহরণস্বরূপ পরিবর্তে ব্যবহার করুন StringIO.StringIODocs.python.org/3/library/io.html
Attila123

StringIOউচ্চ-পারফরম্যান্স ইউনিভার্সাল নিউলাইন হ্যান্ডলিং পেতে ব্যবহার করাও একটি ভাল উপায়।
মার্টিনিউ

3

আমি যদি Modules/cStringIO.cসঠিকভাবে পড়ি তবে এটি বেশ দক্ষ হওয়া উচিত (যদিও কিছুটা ভার্বোজ):

from cStringIO import StringIO

def iterbuf(buf):
    stri = StringIO(buf)
    while True:
        nl = stri.readline()
        if nl != '':
            yield nl.strip()
        else:
            raise StopIteration

3

রেজেক্স-ভিত্তিক অনুসন্ধান কখনও কখনও জেনারেটরের পদ্ধতির চেয়ে দ্রুত হয়:

RRR = re.compile(r'(.*)\n')
def f4(arg):
    return (i.group(1) for i in RRR.finditer(arg))

2
এই প্রশ্নটি একটি নির্দিষ্ট দৃশ্যের বিষয়ে, সুতরাং শীর্ষ-স্কোরিংয়ের উত্তরটির মতো একটি সাধারণ বেঞ্চমার্ক প্রদর্শন করা সহায়ক।
বিজির্ন পোল্লেক্স

1

আমি মনে করি আপনি নিজের রোল করতে পারেন:

def parse(string):
    retval = ''
    for char in string:
        retval += char if not char == '\n' else ''
        if char == '\n':
            yield retval
            retval = ''
    if retval:
        yield retval

এই বাস্তবায়নটি কতটা দক্ষ তা আমি নিশ্চিত নই, তবে এটি কেবল একবার আপনার স্ট্রিংয়ের মাধ্যমে পুনরাবৃত্তি করবে।

মিমি, জেনারেটর।

সম্পাদনা:

অবশ্যই আপনি যে কোনও ধরণের পার্সিং ক্রিয়া নিতে চান তা যুক্ত করতে চাইবেন তবে এটি বেশ সহজ।


দীর্ঘ লাইনের জন্য অত্যন্ত অদক্ষ ( +=অংশটি সবচেয়ে খারাপ ক্ষেত্রে O(N squared)কর্মক্ষমতা রয়েছে, যদিও বেশ কয়েকটি বাস্তবায়ন কৌশল যখন সম্ভব হয় তখন এটি হ্রাস করার চেষ্টা করে)।
অ্যালেক্স মার্টেলি

হ্যাঁ - আমি সম্প্রতি এটি সম্পর্কে শিখছি। চরগুলির তালিকায় যুক্ত হওয়া এবং তারপরে '' .জাইন (অক্ষর) যোগ করা কি আরও দ্রুত হবে? অথবা এটি কি আমার নিজের হাতে নেওয়া উচিত? ;)
ওয়েন ওয়ার্নার

দয়া করে নিজেকে পরিমাপ করুন, এটি শিক্ষণীয় - এবং ওপি'র উদাহরণ এবং লম্বা দুটিয়ের মতো সংক্ষিপ্ত রেখা চেষ্টা করে দেখুন! -)
অ্যালেক্স মার্টেলি

সংক্ষিপ্ত স্ট্রিংগুলির জন্য (<~ 40 অক্ষর) + = প্রকৃতপক্ষে দ্রুততর তবে খুব খারাপ ক্ষেত্রে দ্রুত আঘাত হানে। দীর্ঘতর স্ট্রিংয়ের জন্য, .joinপদ্ধতিটি আসলে ও (এন) জটিলতার মতো দেখায়। যেহেতু আমি এখনও এসও-তে তৈরি করা নির্দিষ্ট তুলনাটি খুঁজে পাইনি, তাই আমি একটি প্রশ্ন শুরু করেছি স্ট্যাকওভারফ্লো.com/ জিজ্ঞাসা / 3055477/… (যে আশ্চর্যজনকভাবে কেবল আমার নিজের চেয়ে বেশি উত্তর পেয়েছে!)
ওয়েন ওয়ার্নার

0

আপনি "একটি ফাইল" দিয়ে পুনরাবৃত্তি করতে পারেন, যা লাইন উত্পাদন করে, পিছনে থাকা নতুন লাইনের চরিত্র সহ। স্ট্রিংয়ের বাইরে একটি "ভার্চুয়াল ফাইল" তৈরি করতে, আপনি ব্যবহার করতে পারেন StringIO:

import io  # for Py2.7 that would be import cStringIO as io

for line in io.StringIO(foo):
    print(repr(line))
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.