Bir metinde bir sayıdan önce ve sonra isim ifadesini bulun

Aug 19 2020

Bir metin verildiğinde, bir check_words listesine (bir tür engellenecek kelimeler) ait bir durdurma kelimesine kadar tüm sayıların önceki kelimeleri bulmalıyım.

Kodum:

check_words = ['the', 'a', 'with','to']
mystring = 'the code to find the beautiful words 78 that i have to nicely check 45 with the snippet'
list_of_words = mystring.split()

Söz konusu metinde daha önce kontrol ederdim '78've '45'check_words'teki kelimelerin herhangi birini bulduğum noktaya kadar geri giderim (ancak 8 kelimeden fazla değil).

Bunu yapmanın kodu şunlar olabilir:

preceding_chunks = []
for i,word in enumerate(list_of_words):
    if any(char.isdigit() for char in word):
       
        # 8 precedent words (taking into account that I can not slice with 8 back at the beginning)
        preceding_words = list_of_words[max(0,i-8):i]
        preceding_words[::-1]

        # I check from the end of the list towards the start
        for j,sub_word in enumerate(preceding_words[::-1]):
            if  sub_word in check_words:
                # printing out j for checking
                myposition = j
                print(j)
                real_preceding_chunk = preceding_words[len(preceding_words)-j:]
                print(real_preceding_chunk)
                preceding_chunks.append(real_preceding_chunk)
                break

Bu kod çalışıyor. temelde her kelimeyi kontrol ediyorum ama (belki yanılıyorum) birkaç gömlek ile ve dolayısıyla döngüler olmadan elde edilebileceği izlenimine sahibim. Herhangi bir fikir?


NOT: Bu soru, kodun okunabilirliğini iyileştirmek, kodu daha hızlı hale getirmek için döngülerden kurtulmaya çalışmak ve Python Zen'in bir parçası olan kodu daha güzel yapmaya çalışmakla ilgilidir.


NOT 2: Yaptığım bazı önceki kontroller:

  • Finding the position of an item in another list from a number in a different list
  • Finding the index of an item in a list
  • Find in list

Yanıtlar

MarioIshac Aug 19 2020 at 09:13

I came up with this:

import itertools
import re

chunks = (grouped_chunk.split() for grouped_chunk in re.split("\\s+\\d+\\s+", mystring))
preceding_chunks = []

for reversed_chunk in map(reversed, chunks):
    preceding_chunk = list(itertools.takewhile(lambda word: word not in check_words, reversed_chunk))[::-1]
    preceding_chunks.append(preceding_chunk)

We apply itertools.takewhile to the reversed_chunk which gives us the preceding chunk in reversed order. We then obtain the correctly ordered preceding_chunk by reversing at the end with [::-1].

The regex splits mystring based on a number (the escaped \d+). The surrounding escaped \s+s represent any padding around the number. This causes this code to have different behavior than yours if digits and letters are mixed in the same words (for example, a1).

For your original code, I'd make a couple suggestions:

  1. Follow PEP 8. For example, add spacing after the comma in i,word.
  2. Remove the redundant expression preceding_words[::-1]. While this does evaluate to the reversed preceding_words, because it is not in-place, the evaluation has no side-effects. Plus, you already perform this reversal in enumerate(preceding_words[::-1]).