一个基于布尔值选择两个值的简短表达式?

huangapple go评论54阅读模式
英文:

A short expression to pick from two values based on bool?

问题

如果一个列表中有两个值

choices = [1,2]

而且想要根据一个名为 `ascending` 的 `bool` 参数将这些值分配给两个变量,可以这样做:

if ascending:
a, b = choices[1], choices[0]
else:
a, b = choices[0], choices[1]

可以使用以下一行代码来表达:

a, b = choices[ascending], choices[not ascending]

或者

a, b = choices[::(-1)**ascending]


我更喜欢最后一种方法,但它可能不够清晰。还有其他更简洁但更可读的方式来实现相同的效果吗?
英文:

If one has two values in a list:

choices = [1,2]

and one wants to assign these values to two variables based on a bool parameter (let's call it ascending) like this:

if ascending:
  a,b = choices[1], choices[0]
else:
  a,b = choices[0], choices[1]

one can express this in one line like this:

a,b  = choices[ascending], choices[not ascending]

or

a,b = choices[::(-1)**ascending]

I like the last one better, but it's probably not extremely transparent. What are other concise but more readable ways to do the same?

答案1

得分: 2

如果原始值始终按升序排列,也许这更能表达您的意图:

a, b = sorted(choices, reverse=ascending)

否则,更简单通常更好(参见Kelly的回答),但我可以想出一种方法使它更加晦涩:

例如:

a, b = (iter, reversed)[ascending](choices)

或者,如果其中一种选项比另一种更有可能,您可以基于那个选项进行赋值,如果是另一种情况,则交换变量:

a, b = choices
if ascending: a, b = b, a
英文:

If the original values are always in ascending order, perhaps this would be more expressive of your intent:

a,b = sorted(choices, reverse=ascending)

Otherwise simpler is generally better (see Kelly's answer) but I could think of ways to make it even more obscure:

For example:

a,b = (iter,reversed)[ascending](choices)

Alternatively, if one option is more likely than the other you could do the assignment based on that one and swap the variables if it's the other case:

a,b = choices
if ascending: a,b = b,a

答案2

得分: 1

以下是代码部分的中文翻译:

from timeit import timeit
from statistics import mean, stdev

codes = '''

if ascending:
  a, b = choices[1], choices[0]
else:
  a, b = choices[0], choices[1]

a, b = choices[ascending], choices[not ascending]

a, b = choices[::(-1)**ascending]

b, a = choices[::ascending or -1]

a, b = choices[::-1 if ascending else 1]

if ascending:
  b, a = choices
else:
  a, b = choices

a, b = choices[::-ascending or 1]

a, b = choices[::-1] if ascending else choices

a, b = reversed(choices) if ascending else choices

'''.strip().split('\n\n')

choices = ['foo', 'bar']
for ascending in False, True:
  print(f'{ascending=}')
  for code in codes:
    a = b = None
    exec(code)
    print(a, b)
  print()

times = {c: ([], []) for c in codes}
def stats(c):
  false, true = (
    (mean(ts), stdev(ts))
    for ts in times[c]
    for ts in [[t*1e9 for t in sorted(ts)[:5]]]
  )
  return false[0] + true[0], false, true

for _ in range(25):
  for ascending in False, True:
    setup = f"choices = ['foo', 'bar']; {ascending = }"
    for c in codes:
      number = 10**5
      t = timeit(c, setup, number=number) / number
      times[c][ascending].append(t)

print('asc = False    asc = True')
for c in sorted(codes, key=stats):
  _, false, true = stats(c)
  # print('{:5.1f} ± {:4.1f}   {:5.1f} ± {:4.1f}  '.format(*false, *true), ' '.join(c.split()))
  print('{:5.1f} ± {:4.1f}   {:5.1f} ± {:4.1f}  '.format(*false, *true), c.strip().replace('\n', '\n' + ' '*30))

注意:这是代码的中文翻译,不包括代码的执行结果。

英文:

Some ways...

b,a = choices[::ascending or -1]
a,b = choices[::-ascending or 1]
a,b = choices[::-1] if ascending else choices
a,b = choices.pop(ascending), *choices

The last one modifies the list.

Or with multiple lines but kinda clean and I suspect fastest:

if ascending:
b,a = choices
else:
a,b = choices

Benchmarks (times in nanoseconds):

 asc = False    asc = True
------------   ------------
30.2 ±  0.3    30.2 ±  0.3   if ascending:
b,a = choices
else:
a,b = choices
60.2 ±  1.3    60.8 ±  2.1   a,b = choices[ascending], choices[not ascending]
62.8 ±  0.6    63.5 ±  0.9   if ascending:
a,b = choices[1], choices[0]
else:
a,b = choices[0], choices[1]
30.3 ±  0.3   125.0 ±  3.7   a,b = choices[::-1] if ascending else choices
30.1 ±  0.1   178.2 ±  4.5   a,b = reversed(choices) if ascending else choices
124.5 ±  3.5   126.1 ±  5.8   b,a = choices[::ascending or -1]
133.6 ±  7.0   123.8 ±  2.6   a,b = choices[::-1 if ascending else 1]
143.4 ±  6.4   139.9 ±  6.1   a,b = choices[::-ascending or 1]
169.4 ± 18.5   441.2 ± 20.4   a,b = choices[::(-1)**ascending]

Code (Attempt This Online!):

from timeit import timeit
from statistics import mean, stdev

codes = '''

if ascending:
  a,b = choices[1], choices[0]
else:
  a,b = choices[0], choices[1]

a,b = choices[ascending], choices[not ascending]

a,b = choices[::(-1)**ascending]

b,a = choices[::ascending or -1]

a,b = choices[::-1 if ascending else 1]

if ascending:
  b,a = choices
else:
  a,b = choices

a,b = choices[::-ascending or 1]

a,b = choices[::-1] if ascending else choices

a,b = reversed(choices) if ascending else choices

'''.strip().split('\n\n')

choices = ['foo', 'bar']
for ascending in False, True:
  print(f'{ascending=}')
  for code in codes:
    a = b = None
    exec(code)
    print(a, b)
  print()

times = {c: ([], []) for c in codes}
def stats(c):
  false, true = (
    (mean(ts), stdev(ts))
    for ts in times[c]
    for ts in [[t*1e9 for t in sorted(ts)[:5]]]
  )
  return false[0] + true[0], false, true

for _ in range(25):
  for ascending in False, True:
    setup = f"choices = ['foo', 'bar']; {ascending = }"
    for c in codes:
      number = 10**5
      t = timeit(c, setup, number=number) / number
      times[c][ascending].append(t)

print('asc = False    asc = True')
for c in sorted(codes, key=stats):
  _, false, true = stats(c)
  # print('{:5.1f} ± {:4.1f}   {:5.1f} ± {:4.1f}  '.format(*false, *true), ' '.join(c.split()))
  print('{:5.1f} ± {:4.1f}   {:5.1f} ± {:4.1f}  '.format(*false, *true), c.strip().replace('\n', '\n' + ' '*30))

huangapple
  • 本文由 发表于 2023年4月7日 00:21:31
  • 转载请务必保留本文链接:https://go.coder-hub.com/75951678.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定