Уведомления

Группа в Telegram: @pythonsu

#1 Янв. 29, 2017 16:41:33

for_soul
Зарегистрирован: 2017-01-25
Сообщения: 13
Репутация: +  0  -
Профиль   Отправить e-mail  

Уменьшить все числа в строке на 1

Здравствуйте.
Есть такая строка:
{9}:{10}:текст:{6}:{7}:{4}text::{5}::::{11}@{12}:{13}
Все числа в ней размещены между фигурных скобок

Задача: написать функцию, которая будет уменьшать все числа в строке на 1.
Чтобы на выходе получить:
{8}:{9}:текст:{5}:{6}:{3}text:{4}::::{10}@{11}:{12}

С учетом того, что в строке есть как минимум одно число в фигурных скобках.
Если есть числа НЕ в фигурных скобках, то их трогать не надо.
В фигурных скобках могут быть только числа.

Отредактировано for_soul (Янв. 29, 2017 16:52:34)

Офлайн

#2 Янв. 29, 2017 17:24:21

for_soul
Зарегистрирован: 2017-01-25
Сообщения: 13
Репутация: +  0  -
Профиль   Отправить e-mail  

Уменьшить все числа в строке на 1

Не смог просто ждать. Набросал функцию. Оцените, пожалуйста.
Работает вроде правильно, с данной строкой.

 out_stroka = '{9}:{10}:http:{6}:{7}:{4}:{5}::::{11}@{12}:{13}'
def minus_1(stroka):
    list_out = []
    list_in = re.split('}|{', stroka)
    for _ in list_in:
        if _.isdigit():
            list_out.append('{' + str(int(_) - 1) + '}')
        else:
            list_out.append(_)
    return ''.join(list_out)
print(minus_1(out_stroka))

Офлайн

#3 Янв. 29, 2017 17:57:20

py.user.next
От:
Зарегистрирован: 2010-04-29
Сообщения: 9993
Репутация: +  857  -
Профиль   Отправить e-mail  

Уменьшить все числа в строке на 1

  
>>> import re
>>> 
>>> s = '{9}:{10}:текст:{6}:{7}:{4}text::{5}::::{11}@{12}:{13}'
>>> 
>>> def tr(s):
...     return '{}{}{}'.format(s[0], int(s[1:-1]) - 1, s[-1]) 
... 
>>> out = re.sub(r'\{\d+\}', lambda mo: tr(mo.group()), s)
>>> out
'{8}:{9}:текст:{5}:{6}:{3}text::{4}::::{10}@{11}:{12}'
>>>

Add
30 Jan 2017 19:07:41
Это обновление функции трансляции, исправляющее баг с числами вида {01}.
  
>>> import re
>>> 
>>> s = '{9}:{10}:текст:{6}:{7}:{4}text::{5}::::{11}@{12}:{13}'
>>> 
>>> def tr(s):
...     num = s[1:-1]
...     if num[0] == '0' and len(num) > 1:
...         out = s
...     else:
...         out = '{}{}{}'.format(s[0], int(num) - 1, s[-1])
...     return out
... 
>>> out = re.sub(r'\{\d+\}', lambda mo: tr(mo.group()), s)
>>> out
'{8}:{9}:текст:{5}:{6}:{3}text::{4}::::{10}@{11}:{12}'
>>>

for_soul
Оцените, пожалуйста.
Ну, он даст ошибку на строке “}10{”, это если сходу его ломать (там ещё много строк можно придумать), не смотря на чисто питоновские ошибки вроде использования подчёркивания там, где не принято это делать.

  
>>> def minus_1(stroka):
...     list_out = []
...     list_in = re.split('}|{', stroka)
...     for _ in list_in:
...         if _.isdigit():
...             list_out.append('{' + str(int(_) - 1) + '}')
...         else:
...             list_out.append(_)
...     return ''.join(list_out)
... 
>>> minus_1('}10{')
'{9}'
>>>



Отредактировано py.user.next (Янв. 30, 2017 10:09:30)

Офлайн

#4 Янв. 29, 2017 19:29:05

for_soul
Зарегистрирован: 2017-01-25
Сообщения: 13
Репутация: +  0  -
Профиль   Отправить e-mail  

Уменьшить все числа в строке на 1

py.user.next
Спасибо. Работает.
Но никак не могу понять, что делает эта строчка, вернее как она это делает.
На просторах интернета так и не нашел внятного пояснения метода group()
Не могли бы вкратце объяснить?
 lambda mo: tr(mo.group())

Отредактировано for_soul (Янв. 29, 2017 21:04:12)

Офлайн

#5 Янв. 29, 2017 23:00:22

vic57
Зарегистрирован: 2015-07-07
Сообщения: 913
Репутация: +  127  -
Профиль  

Уменьшить все числа в строке на 1

можно проще:

 #
s = '{9}:{10}:http:{6}:{7}:{4}:{5}::10.05:текст:{11}@{12}:{13}'
tmp = ''
out = ''
for i in s:
    tmp += i
    if i == '{' :
        out += tmp
        tmp = '' 
    elif i == '}':
        try : out += str(int(tmp[:-1]) - 1 ) + '}'
        except: out += tmp
        tmp = ''
out += tmp
print s
print out

Отредактировано vic57 (Янв. 29, 2017 23:22:31)

Офлайн

#6 Янв. 30, 2017 03:30:44

py.user.next
От:
Зарегистрирован: 2010-04-29
Сообщения: 9993
Репутация: +  857  -
Профиль   Отправить e-mail  

Уменьшить все числа в строке на 1

vic57
можно проще:
  
>>> def f(s):
...     tmp = ''
...     out = ''
...     for i in s:
...         tmp += i
...         if i == '{' :
...             out += tmp
...             tmp = ''
...         elif i == '}':
...             try : out += str(int(tmp[:-1]) - 1 ) + '}'
...             except: out += tmp
...             tmp = ''
...             out += tmp
...     return out
... 
>>> s = '{9}:{10}:http:{6}:{7}:{4}:{5}::10.05:текст:{11}@{12}:{13}'
>>> 
>>> print s
{9}:{10}:http:{6}:{7}:{4}:{5}::10.05:текст:{11}@{12}:{13}
>>> print f(s)
{8}:{9}:http:{5}:{6}:{3}:{4}::10.05:текст:{10}@{11}:{12}
>>> 
>>> print f('{abc')
{
>>>
Тут портит строку, если закрывающей скобки нет.


for_soul
На просторах интернета так и не нашел внятного пояснения метода group()
Не могли бы вкратце объяснить?
re.sub() когда находит совпадение, это совпадение представляет из себя объект, в котором хранится вся информация о совпадении (где оно в строке находится, какая подстрока совпала, какие там группы есть). Вот этот объект потом пытается замениться. Если ему предлагают замениться на строку, то он просто отбрасывается и вместо него вставляется строка. А если ему предлагают замениться по функции, то эта функция считается такого вида “принимает объект совпадения и возвращает какую-то строку”, потом исходный объект совпадения отбрасывается и вместо него вставляется эта возвращённая из функции строка.

Так вот, чтобы функцию не делать зависящей от объекта совпадения (может она нам понадобится в других задачах, где нет регулярных выражений вообще), мы делаем обёртку - маленькую функцию, которая просто берёт из объекта совпадения совпавшую строку и уже эту строку подаёт в какую-то функцию преобразования строки (трансляции строки, почему и tr). Поэтому мы функцию преобразования строки можем использовать в любой другой задаче, она никак не связана с регулярными выражениями, она просто преобразует (транслирует) строку в строку.

  
lambda mo: tr(mo.group())
lambda - это выражение для создания анонимной функции (функции без имени).

mo - это аргумент этой анонимной функции, мы его назвали mo потому, что он обозначает match object (объект совпадения).

tr() - это вызов нашей функции трансляции строки, она принимает строку “{10}” и возвращает строку “{9}” (ну, это частный случай, а вообще “{N}” -> “{N-1}”, так будет правильнее записать).

mo.group() - это вызов у объекта совпадения метода group() для получения всей строки, с которой совпало регулярное выражение. Метод group() у объекта совпадения может брать любые группы, которые совпали (типа mo.group(1, 2)), а просто group() равно group(0), где нулевая группа - это вся строка, которая совпала с регулярным выражением.


И когда анонимная функция запустилась, она достаёт строку у объекта совпадения и вызывает функцию трансляции, передавая в неё эту строку; затем функция трансляции её транслирует в новую строку и новая строка возвращается из анонимной функции и передаётся функции re.sub() для замены; и та её берёт и вставляет вместо совпадения.

А это пример, как выглядит работа с группами в объекте совпадения
  
>>> import re
>>> 
>>> mo = re.search(r'(a)(b)(c)', 'abcdef')
>>> mo.group(0)
'abc'
>>> mo.group()
'abc'
>>> mo.group(1)
'a'
>>> mo.group(2)
'b'
>>> mo.group(3)
'c'
>>> mo.group(1, 2, 3)
('a', 'b', 'c')
>>> mo.group(1, 2, 3, 1, 2, 3)
('a', 'b', 'c', 'a', 'b', 'c')
>>> 
>>> mo.groups()
('a', 'b', 'c')
>>>
>>> mo
<_sre.SRE_Match object at 0xb73e5660>
>>>

Дальше будешь читать python.org. re.sub и python.org. match object



Отредактировано py.user.next (Янв. 30, 2017 03:43:34)

Офлайн

#7 Янв. 30, 2017 08:26:46

vic57
Зарегистрирован: 2015-07-07
Сообщения: 913
Репутация: +  127  -
Профиль  

Уменьшить все числа в строке на 1

py.user.next
строку пропустили после цикла, ничего не портит

 s = '{9}:{21}:http:{6}:{7}:{4}:{5}::10.05:текст:{11}@{12}:{13}:qqq{AS}DFФЫА{{{'    
def f(s):
    tmp = ''
    out = ''
    for i in s:
        tmp += i
        if i == '{' :
            out += tmp
            tmp = '' 
        elif i == '}':
            try : out += str(int(tmp[:-1]) - 1 ) + '}'
            except: out += tmp
            tmp = ''
    out += tmp #остаток строки
    return out
print 's:',s
print 'out:',f(s)

Отредактировано vic57 (Янв. 30, 2017 08:29:44)

Офлайн

#8 Янв. 30, 2017 10:11:47

py.user.next
От:
Зарегистрирован: 2010-04-29
Сообщения: 9993
Репутация: +  857  -
Профиль   Отправить e-mail  

Уменьшить все числа в строке на 1

vic57
строку пропустили после цикла, ничего не портит
Не, это ты поправил просто. Ну, я тогда серьёзно взялся проверить твой код :)
(Заодно и в своём нашёл бажок небольшой и поправил там выше.)

Твой код
  
def f(s):
    tmp = ''
    out = ''
    for i in s:
        tmp += i
        if i == '{' :
            out += tmp
            tmp = '' 
        elif i == '}':
            try : out += str(int(tmp[:-1]) - 1 ) + '}'
            except: out += tmp
            tmp = ''
    out += tmp #остаток строки
    return out

Вот твои баги
[guest@localhost bracenum]$ python -mdoctest bracenum1.doct
**********************************************************************
File "bracenum1.doct", line 30, in bracenum1.doct
Failed example:
f('{1 }')
Expected:
'{1 }'
Got:
'{0}'
**********************************************************************
File "bracenum1.doct", line 32, in bracenum1.doct
Failed example:
f('{ 1}')
Expected:
'{ 1}'
Got:
'{0}'
**********************************************************************
File "bracenum1.doct", line 34, in bracenum1.doct
Failed example:
f('{\n1}')
Expected:
'{\n1}'
Got:
'{0}'
**********************************************************************
File "bracenum1.doct", line 36, in bracenum1.doct
Failed example:
f('{1\n}')
Expected:
'{1\n}'
Got:
'{0}'
**********************************************************************
File "bracenum1.doct", line 41, in bracenum1.doct
Failed example:
f('{00}')
Expected:
'{00}'
Got:
'{-1}'
**********************************************************************
File "bracenum1.doct", line 43, in bracenum1.doct
Failed example:
f('{01}')
Expected:
'{01}'
Got:
'{0}'
**********************************************************************
1 items had failures:
6 of 20 in bracenum1.doct
***Test Failed*** 6 failures.
[guest@localhost bracenum]$



Отредактировано py.user.next (Янв. 30, 2017 10:14:37)

Офлайн

#9 Янв. 30, 2017 11:07:52

vic57
Зарегистрирован: 2015-07-07
Сообщения: 913
Репутация: +  127  -
Профиль  

Уменьшить все числа в строке на 1

py.user.next
1.поправил я еще вчера,что видно по времени правки. ты был в офлайне
2.по условию ТС в скобках только цифры, без всяких пробелов

Офлайн

#10 Янв. 30, 2017 12:27:31

py.user.next
От:
Зарегистрирован: 2010-04-29
Сообщения: 9993
Репутация: +  857  -
Профиль   Отправить e-mail  

Уменьшить все числа в строке на 1

vic57
1.поправил я еще вчера,что видно по времени правки.
Ладно, наверное, я ошибся и что-то там передвинул.

vic57
2.по условию ТС в скобках только цифры, без всяких пробелов
Ну, мы можем достроить просто задачу до общей (где всё-таки нет этого ограничения), чтобы соблюсти правило слабого предусловия (слабое предусловие делает функцию применимой для решения большего числа задач). Просто тестирование через исключение при приведению к int нехорошая практика, так как зависит от семантики функции, у которой вообще своё понимание чисел, например int(“aa”, 16) и прочее. И эти пробелы, которые она может принимать, - как раз доказательство этому (не предназначена она для таких точных проверок, только для преобразований). Даже isdigit() здесь даст более точную картину. Ну, и числа вида 002 твоя версия превращает в 1, а куда ведущие нули тогда деваются? Хотя я за то, чтобы вообще такие числа не трогать, а считать их нечисловой лексемой, как и {+1} и подобные.



Отредактировано py.user.next (Янв. 30, 2017 12:34:08)

Офлайн

Board footer

Модераторировать

Powered by DjangoBB

Lo-Fi Version