如何在不使用列表的情况下转换字符串中的数字?

Posted

技术标签:

【中文标题】如何在不使用列表的情况下转换字符串中的数字?【英文标题】:How to convert numbers in a string without using lists? 【发布时间】:2017-02-18 03:44:37 【问题描述】:

我的教授希望我创建一个函数,该函数返回字符串中数字的总和,但不使用任何列表或列表方法。

函数运行时应该是这样的:

>>> sum_numbers('34 3 542 11')
    590

当使用列表和列表方法时,通常这样的函数很容易创建。但是试图在不使用它们的情况下这样做是一场噩梦。

我尝试了以下代码,但它们不起作用:

 >>> def sum_numbers(s):
    for i in range(len(s)):
        int(i)
        total = s[i] + s[i]
        return total


>>> sum_numbers('1 2 3')
'11'

我没有将 1、2 和 3 全部转换为整数并相加,而是得到字符串 '11'。也就是说,字符串中的数字还没有转化为整数。

我也尝试使用map() 函数,但我得到了相同的结果:

>>> def sum_numbers(s):
    for i in range(len(s)):
        map(int, s[i])
        total = s[i] + s[i]
        return total


>>> sum_numbers('1 2 3')
'11'

【问题讨论】:

当您说“不使用任何列表或列表方法”时,这是否包括"1 2 3".split() 之类的内容? (技术上str.split() 创建一个列表,但您不必显式存储它。) 差不多。我将 split() 用于允许它的不同问题。在作业表上明确写着“要求:不要使用列表”。 我提供了一个答案(我认为它可以解决您的所有问题!)。但我想评论一下您对map 的使用。 map 接受一个函数和一个可迭代对象(如列表)并返回一个列表,其中列表的每个元素都应用了函数。想象一个函数def add_one(x): return x + 1,然后执行map(add_one, [1, 2, 3]) 将返回[2, 3, 4] @PierceDarragh,在 Python3 中,map() 不会像 range() 那样返回列表——为什么要选择它?我同意使用map() 不符合练习的精神,但话说回来,我对range()sum()re.finditer() 也有这种感觉! @cdlane 我很困惑......我不是在谈论map 是否类似于列表。 OP 的示例尝试在单个字符上使用 map ......这是行不通的。所以我解释了map 的工作原理,因为在我看来 OP 并不真正知道如何使用它。 【参考方案1】:

当然很傻,但为了好玩:

s = '34 3 542 11'

n = ""; total = 0
for c in s:
    if c == " ":
        total = total + int(n)
        n = ""
    else:
        n = n + c
# add the last number
total = total + int(n)

print(total)
> 590

这假定所有字符(除了空格)都是数字。

【讨论】:

@PierceDarragh 你使用了split(),它返回一个列表。这意味着它不符合问题的目标。 @roganjosh 我认为您没有阅读我的实际解决方案...最后的一点是一个单行字,以显示 I 会做什么。跨度> @roganjosh 不用担心!我总是看错东西,哈哈。 :) 如果输入为空或有前导或尾随空格,此代码将失败。最好用isdigit()进行测试。 @JacobVlijm。如果你使用isdigit(),所有的空格问题都会消失。【参考方案2】:

您肯定在这里付出了一些努力,但是您的方法的一部分肯定不会按原样工作:您正在迭代字符串中的 characters,但您一直在尝试将每个字符视为自己的数字。我编写了一个(非常注释的)方法,它可以在不使用任何列表或列表方法的情况下完成您想要的操作:

def sum_numbers(s):
    """
    Convert a string of numbers into a sum of those numbers.

    :param s: A string of numbers, e.g. '1 -2 3.3 4e10'.
    :return: The floating-point sum of the numbers in the string.
    """
    def convert_s_to_val(s):
        """
        Convert a string into a number. Will handle anything that
        Python could convert to a float.

        :param s: A number as a string, e.g. '123' or '8.3e-18'.
        :return: The float value of the string.
        """
        if s:
            return float(s)
        else:
            return 0
    # These will serve as placeholders.
    sum = 0
    current = ''
    # Iterate over the string character by character.
    for c in s:
        # If the character is a space, we convert the current `current`
        # into its numeric representation.
        if c.isspace():
            sum += convert_s_to_val(current)
            current = ''
        # For anything else, we accumulate into `current`.
        else:
            current = current + c
    # Add `current`'s last value to the sum and return.
    sum += convert_s_to_val(current)
    return sum

就我个人而言,我会使用这种单线,但它使用str.split()

def sum_numbers(s):
    return sum(map(float, s.split()))

【讨论】:

这是唯一真正强大的解决方案(尽管检查非数字可能超出了问题的范围)。 @ekhumoro,它在负数上爆炸了。 @cdlane 我已经修改它以考虑负数 @cdlane。当然 - 没有一个答案考虑到浮点数、科学记数法或其他任何可能被合理解释为“数字”的东西;-) @ekhumoro 既然你这么说,我更新了我的提交以说明这些事情。 :)【参考方案3】:

在生成此答案时没有使用(也没有损害)列表:

def sum_string(string):
    total = 0

    if len(string):
        j = string.find(" ") % len(string) + 1
        total += int(string[:j]) + sum_string(string[j:])

    return total

如果字符串比 OP 指示的更嘈杂,那么这应该更健壮:

import re

def sum_string(string):
    pattern = re.compile(r"[-+]?\d+")

    total = 0

    match = pattern.search(string)

    while match:

        total += int(match.group())

        match = pattern.search(string, match.end())

    return total

示例

>>> sum_string('34 3 542 11')
590
>>> sum_string('   34    4   ')
38
>>> sum_string('lksdjfa34adslkfja4adklfja')
38
>>> # and I threw in signs for fun
... 
>>> sum_string('34 -2 45 -8 13')
82
>>> 

【讨论】:

列表切片不会被视为列表方法吗? @PierceDarragh,切片是对序列的操作,包括字符串。字符串切片返回一个子字符串,而不是一个列表。 这很公平!我想我选择在我的解决方案中完全避免任何类似列表的东西,因为我不确定 OP 的教授希望他们采取“没有列表和没有列表方法”的事情有多远。不过,我喜欢您的解决方案对问题采取了不同的方法! 如果输入有前导空格,或者如果有多个空格字符运行,此代码将失败。 @ekhumoro,我添加了一个替代解决方案,可以处理 OP 没有提到的许多此类事情。【参考方案4】:

如果您希望能够处理浮点数和负数:

def sum_numbers(s):
    sm = i = 0
    while i < len(s):
        t = ""
        while  i < len(s) and not s[i].isspace():
            t += s[i]
            i += 1
        if t:
            sm += float(t)
        else:
            i += 1
    return sm

适用于所有情况:

In [9]: sum_numbers('34 3 542 11')
Out[9]: 590.0

In [10]: sum_numbers('1.93 -1 23.12 11')
Out[10]: 35.05

In [11]: sum_numbers('')
Out[11]: 0

In [12]: sum_numbers('123456')
Out[12]: 123456.0

或切片的变体:

def sum_numbers(s):
    prev = sm = i = 0
    while i < len(s):
        while i < len(s) and not s[i].isspace():
            i += 1
        if i > prev:
            sm += float(s[prev:i])
            prev = i
        i += 1
    return sm

您也可以使用 itertools.groupby,它不使用列表,使用一组允许的字符进行分组:

from itertools import groupby


def sum_numbers(s):
    allowed = set("0123456789-.")
    return sum(float("".join(v)) for k,v in groupby(s, key=allowed.__contains__) if k)

给你同样的输出:

In [14]: sum_numbers('34 3 542 11')
Out[14]: 590.0

In [15]: sum_numbers('1.93 -1 23.12 11')
Out[15]: 35.05

In [16]: sum_numbers('')
Out[16]: 0

In [17]: sum_numbers('123456')
Out[17]: 123456.0

如果您只需要考虑正整数,则可以使用 str.isdigit 作为键:

def sum_numbers(s):
    return sum(int("".join(v)) for k,v in groupby(s, key=str.isdigit) if k)

【讨论】:

【参考方案5】:

试试这个:

def sum_numbers(s):
    sum = 0
    #This string will represent each number
    number_str = ''
    for i in s:
        if i == ' ':
            #if it is a whitespace it means
            #that we have a number so we incease the sum
            sum += int(number_str)
            number_str = ''
            continue
        number_str += i
    else:
        #add the last number
        sum += int(number_str)
    return sum

【讨论】:

这与我的解决方案类似,但您的解决方案根本不处理非空格非数字字符。 @PierceDarragh 它假定输入是正确的,但我使用 for/else 只是为了在函数中添加最后一个数字并提出其他解决方案。 for/else 很好,但我认为更好的解决方案是 (1) 检查多种类型的空格和 (2) 可能处理输入中的错误。 @PierceDarragh 我只是想向操作员展示这种机制的存在。我从未说过它是最佳的,但它很漂亮。此外,操作员学习新的有用的东西也不错。 【参考方案6】:

你可以写一个生成器:

def nums(s):
    idx=0
    while idx<len(s):
        ns=''
        while idx<len(s) and s[idx].isdigit():
            ns+=s[idx]
            idx+=1
        yield int(ns)
        while idx<len(s) and not s[idx].isdigit():
            idx+=1

>>> list(nums('34 3 542 11'))
[34, 3, 542, 11]

然后总结一下:

>>> sum(nums('34 3 542 11')) 
590

或者,您可以将re.finditer 与正则表达式和生成器构造一起使用:

>>> sum(int(m.group(1)) for m in re.finditer(r'(\d+)', '34 3 542 11'))
590

没有使用列表...

【讨论】:

【参考方案7】:
def sum_numbers(s):
    total=0
    gt=0 #grand total
    l=len(s)
    for i in range(l):
        if(s[i]!=' '):#find each number
            total = int(s[i])+total*10
        if(s[i]==' ' or i==l-1):#adding to the grand total and also add the last number
            gt+=total
            total=0
    return gt

print(sum_numbers('1 2 3'))

这里将每个子字符串转换为数字并添加到授予总数

【讨论】:

请提供一些评论以解释此代码的工作原理以及它如何回答问题。 虽然此答案可能是正确且有用的,但如果您 include some explanation along with it 解释它如何帮助解决问题,则最好。这在未来变得特别有用,如果有一个变化(可能不相关)导致它停止工作并且读者需要了解它曾经是如何工作的。【参考方案8】:

如果我们忽略 eval 是 evil 这个事实,我们可以用它来解决这个问题。

def sum_numbers(s):
    s = s.replace(' ', '+')
    return eval(s)

是的,就这么简单。但我不会把那个东西投入生产。

当然我们需要测试一下:

from hypothesis import given
import hypothesis.strategies as st


@given(list_num=st.lists(st.integers(), min_size=1))
def test_that_thing(list_num):
    assert sum_numbers(' '.join(str(i) for i in list_num)) == sum(list_num)

test_that_thing()

而且它不会引发任何问题。

【讨论】:

以上是关于如何在不使用列表的情况下转换字符串中的数字?的主要内容,如果未能解决你的问题,请参考以下文章

如何在不丢失小数的情况下将数据框中的字符转换为数字

如何将字符串数字转换为列表中的整数?

如何在不使用 ToString() 的情况下将 Int 转换为 C# 中的字符串?

如何在不删除先前相同值的情况下选择具有重复项的列表中的特定数字?

如何在不调用索引函数或任何内置函数的情况下获取列表中的索引?

如何在不使用变换或上/左的情况下转换列表中项目的位置