如何在 Python 3.5.1 中使用 *args 在列表中进行计算
Posted
技术标签:
【中文标题】如何在 Python 3.5.1 中使用 *args 在列表中进行计算【英文标题】:How to do calculations in lists with *args in Python 3.5.1 【发布时间】:2017-02-11 19:12:25 【问题描述】:我什至不知道如何问这个问题;我已经做了大约一个月的编程,我在这个程序上取得的进展仍然有点超出我的想象,所以如果这个问题有点难以理解,我很抱歉。我正在上编程课,但据我所知,这个程序只是为了我自己的功能和音乐享受。
我正在尝试编写一个函数,该函数采用整数表示的音符,并吐出这些数字的特定排列和转换。具体来说,我正在尝试将它们转换为音乐集理论中称为“原始形式”的东西。
我编写了一个函数,它成功地为一组三个音高做到了这一点,但我想扩展它,以便我可以为函数设置任意数量的参数。
这是第一个函数的代码
def prime_form (pitch1, pitch2, pitch3, tones_in_octave = 12):
"""
(int, int, int, int) -> (str)
finds the prime form of any 3 note pitch set in any equal temperament,
one octave span of frequency classes (default is 12 tones).
>>> prime_form (9, 1, 5)
(0 4 8)
this is the prime form!
>>> prime_form (11, 1, 0)
(0 1 2)
this is the prime form!
>>> prime_form (1, 3, 4)
(0 1 3)
this is the prime form!
"""
pitches = [pitch1 %tones_in_octave, pitch2 %tones_in_octave, pitch3 %tones_in_octave]
spitches = sorted(pitches)
intervals = [(spitches[1] - spitches[0]) %tones_in_octave, (spitches[2] - spitches[1]) %tones_in_octave, (spitches [0] - spitches[2]) %tones_in_octave]
sintervals = sorted(intervals)
prime_form = [0, sintervals[0], sintervals[0]+sintervals[1]]
print('( )\nthis is the prime form!'.format(prime_form[0], prime_form[1], prime_form[2]))
我学到了一些关于 *args 的知识,在比我聪明得多的程序员的帮助下,我最终得到了这个结果。
def prime_form (*pitches, tones_in_octave = 12):
tones_in_octave=int(tones_in_octave)
spitches = list(set(sorted([pitch %tones_in_octave for pitch in list(pitches)])))
print(spitches)
与最后一个程序并行,到目前为止,这可以完成我需要它做的事情,直到我开始定义“间隔”的部分,但我不知道如何去做。我想我可以写很多 if 语句,但理论上如果我想让它对 tones_in_octave = 12
起作用,那么我将不得不写很多 if
和 elif
语句,即使那样如果我想超越那个(甚至有一个既定的音乐系统在一个八度音程中使用43
音调,尽管该特定系统不会从这个功能中受益),我将不得不手动编写一堆它们,即使那样我的函数会在我决定停止的时候停止工作。
在这种情况下,我想在代码中直接写出的是
sintervals = sorted([(spitches[1] - spitches[0]) %tones_in_octave, (spitches[2] - spitches[1]) %tones_in_octave, …, (spitches[n-1] - spitches[n-2]) %tones_in_octave, (spitches[0] - spitches[-1]) %tones_in_octave])
其中 n 是列表 spitches
中的项目数(与 *pitches 中的项目不同,因为定义 spitches
的方式删除了冗余值)
问题1.如何在python中定义一个变量作为这个列表?
之后,像这样添加间隔会产生素数形式
prime_from = [0, sintervals[0], sintervals[0] + sintervals[1], sintervals[0] + sintervals[1] + sintervals [2], ..., sintervals[0] + sintervals[1] + ... + sintervals[n-2]]
其中 n 是 sintervals
中的项目数(以及此列表中的项目数)
问题 2。如何根据问题 1 的列表中的项目数量添加这样的数字?
编辑:
这是对最终要完成的工作的解释,如果不是最清楚的,很抱歉(它甚至可能完全是错误的 ahahaha,希望不要)。
---背景:音高和八度---
在最常用的音乐系统中,空气振动的特定频率称为“音高”;音高频率的计算公式为
(参考)*2^(与参考的距离,以音高/以八度为单位)
一个“八度”是一个 (2^x):1 的频率关系,其中 x 是一个整数,x = “八度的数量”;这种关系很重要,因为我们人类感知到与八度音阶相关的空气振动频率/这个比率或多或少是相同的。在这个音乐系统中,最常用于参考音高的频率是“A 440”,意思是“A4”是频率 440Hz。 “A4”中的 4 是音符所在的“八度音域音域”的音域编号。八度音阶音域在音高 C 上划分,因此 A4 是“高于”或“频率高于”C4 的 A(其频率为 (440)^-9/12 = ~261.6Hz) 例如。 A0 = 27.5Hz, A1 = 55Hz, A2 = 110Hz 等等(你可以看出我们仍然使用的字母符号系统是在测量空气振动频率之前构思的)
---背景:音乐理论---
在音乐理论中,集合论是用于研究音乐理论的成熟工具之一。音乐集理论将音高抽象为整数。它通常也忽略八度音域,将A4完全等同于A2,A3,A6等。在上述最流行的音乐系统中,一个八度音程中有12个音调,因此Set Theory中参考音高的方式从(C , C#, D, D#, E, F, F#, G, G#, A, A#, B) 到 (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11) ;为了确保每个人都在同一页面上,当八度音阶寄存器被视为音高的无关信息时,有一个名称,称为“音高类”。对于音高课程,所有数学运算都在 mod 12 中完成,就像钟面一样,因为一个八度有 12 个音调。
同样在集合论中,有一种有用的方法可以列出一组音高类(“音高类集”),称为“素数形式”。素数形式,也称为集合类,被认为是表示音类集合的最简洁和抽象的方式。
---我正在努力完成的事情---
我想编写吃音高类集并吐出集类/素数形式的代码。注意,素数形式必须从 0 开始,如果它满足其他要求(考虑到集合的反转,最左边紧致)并且不从 0 开始,那么将被转置以从 0 开始。 [1,2,4] 是考虑到它的反转 [1,3,4] 的最左紧凑排列,但不是从零开始,因此集合由 (-1) 转置为 (0 1 3),这是它的素数从。试图找出素数形式的人通常需要几个步骤才能得出答案:
删除多余的音高类 - 如果您得到类似 (1, 13, 0, 2, 0) 的音高,请删除 13,因为它与 1 (13%12 = 1) 是多余的,并删除第二个 0。
在单个八度跨度内安排音级 - (不一定是八度音域;八度音域被理解为始终基于音高 C,八度音程跨度是指带有 2 的任意两个音符的跨度: 1 频率关系,而不仅仅是两个 C 相隔一个八度音程。)。例如,我们将 (10, 9, 0, 11) 安排为 (0, 9, 10, 11) (从零开始/在八度音阶寄存器内)或 [9,10,11,0] (普通形式/leftmost-compact 排列)或其他一些可能没那么有用的方式。
查找音程 - 在这种情况下,音程是一个八度中相邻音级差异的绝对值:9-10 或 10-9 = 1, 10-11 = 1, 0-11 = 1(0 需要暂时被认为是 12;这是数字跨越 mod 12 的“钟面”的循环点并搞砸数学的情况)和 0-9(0 和 0 一样好)这里因为它没有越过循环点;如果 0 被认为是 12,你得到错误的区间) = 9。此时,第一个和最后一个区间的差,在这种情况下为 9,也被认为是如果有一个像 [3,4,7,9,0] 或 [1,2,4,5,7,8,10,11] 的集合,其中间隔之间存在联系。所以不能忽略最后一个区间。
根据反转和最左边紧致性重新列出区间 - 这就是我们找到区间的原因;音程抽象了实际的音高,因此我们不必将其转置为 0(因为素数形式的要求是列表从 0 开始),因此我们可以在“正常”之间以最左边的紧凑方式列出我们的音程" 和 "倒置" 间隔列表(在表盘上顺时针和逆时针)来计算素数形式。人类这样做的方式是从音程列表中的一个点开始,然后从该点向左或向右重新列出它,保持音程与它们对应的音高相同的顺序,并在必要时循环。
-
选择正确的区间列表并计算素数形式 -
(0,9,10,11)的区间列表是(9,1,1,1),我们这里做的是选择最左边的紧凑版本,忽略9,因为它是最不紧凑的,然后使用构建素数形式的间隔,即 (0 1 2 3)
对于 (9,11,1,2,4,6,8),区间列表为 (2,2,1,2,2,2,1)。最左边的紧致是 (1, 2, 2, 1, 2, 2, 2),所以素数形式是 (0 1 3 5 6 8 10)。
但是,在这最后一步存在一些分歧,分歧在于 Rahn 和 Forte 算法之间。这是因为我列出的标准在五个特定情况下会产生两个答案,因为在这些情况下,问题是“什么是最左边紧致的?”需要更多的定义。老实说,我不知道将tones_in_octave 更改为12 以外的值是否会改变这些例外情况是否符合我提出的标准。我也不知道如何解释这两种算法在音程方面的差异,因为这些算法是根据从 0 开始的某些音高音程定义的,这会导致最左边紧凑的音程之间存在分歧,而不是音程的实际顺序。如果我想完成这个程序,我想我应该继续努力。
Python 似乎擅长按照 Stack Overflow 和其他来源(可能除了间隔列表排序)所展示的这些步骤,这只是弄清楚如何充分概括它以使函数工作的问题对于可能无限量的论点。感谢 Moses Koledoye 通过泛化函数以使用 *args 完成了大部分繁重的工作,似乎只有一个问题尚未解决,即按照上面步骤 4 中显示的方式对区间列表进行排序。 (其余步骤工作得很好;Koledoye 的第 3 步版本给出了错误的间隔,但将 mod 12 应用于他提出的答案有时会修复第 4 步碰巧正确运行的情况(有时会搞砸)上)。
这是带有缺陷的第 4 步的代码,打印消息以通知刚刚定义的内容,最后还有一个额外的步骤,即有时修复第 3 步无法正常工作的情况,我猜想,数学跨越“循环点”/“钟面”,而其他时候,当它与 Koledoye 的代码正确时,它就会搞砸。
def prime_form (*pitches, tones_in_octave = 12):
tones_in_octave=int(tones_in_octave)
spitches = list(set(sorted([pitch %tones_in_octave for pitch in list(pitches)])))
print('\nthis is the sorted list of pitches \n'.format(spitches))
lth = len(spitches)
print('\nthis is the length of spitches \n'.format(lth))
intervals = [(spitches[0 if (i+1) >= lth else (i+1)]-x) % tones_in_octave for i, x in enumerate(spitches)]
print('\nthis is the interval list \n'.format(intervals))
sintervals = sorted(intervals)
print('\nthis is the sorted interval list \n'.format(sintervals))
p_form = [sum(sintervals[:i]) for i in range(len(intervals))]
print('\nthis may be the prime form, but maybe not \n'.format(p_form))
true_p_form = list(i % tones_in_octave for i in p_form)
print('()\nthis is the prime form! (apparently it might not be)'.format(' '.join(map(str, true_p_form))
这里有一些例子可以尝试
>>> prime_form(9, 3, 0)
(0 3 6)
this is the prime form!
>>> prime_form(1, 11, 9, 4, 2, 6, 8)
(0 1 3 5 6 8 10)
this is the prime form!
>>> prime_form(10, 9, 0, 11)
(0 1 2 3)
this is the prime form!
这是一个计算器,它已经完成了大部分以及更多功能(除了改变八度音阶中的音调数量) http://composertools.com/Tools/PCSets/setfinder.html
它甚至解释了 Rahn 和 Forte 算法之间的区别。
【问题讨论】:
【参考方案1】:以下内容几乎可以满足您的需求。
棘手的部分是计算完成的间隔
使用enumerate
从下一个索引的项目中减去当前项目,并从第一个项目中减去最后一个项目。
通过求和计算的sintervals
的切片来获取累积值。每个切片的停止索引比上一个增加1:
最后一部分是格式化部分,它使用.join
加入列表项,然后对其进行格式化。
def prime_form(*pitches, tones_in_octave=12):
# pitches = set(pitches)
spitches = sorted([p % tones_in_octave for p in pitches])
lth = len(spitches)
# Question 1
intervals = [(spitches[0 if (i+1) >= lth else (i+1)]-x) % tones_in_octave for i, x in enumerate(spitches)]
sintervals = sorted(intervals)
# Question 2
p_form = [sum(sintervals[:i]) for i in range(len(intervals))]
print('()\nthis is the prime form!'.format(' '.join(map(str, p_form))))
测试:
>>> prime_form(9, 1, 5)
(0 4 8)
this is the prime form!
>>> prime_form(11, 1, 0)
(0 1 2)
this is the prime form!
>>> prime_form(1, 3, 4)
(0 1 3)
this is the prime form!
【讨论】:
如果downvoter可以发表评论会很好 顺便说一句,非常感谢! (我仍在努力理解所有这些东西,这太棒了)代码有几个问题,因为我很傻,不能很好地解释这个主题。我也应该提供测试。问题 1. 生成区间列表时,有时会给出太大的区间,例如 prime_form(9,0,3) 的情况。 [9,6,9] 而不是 [3,6,3],因为 sorted() 不会像我希望的那样平等对待 0。一旦它排序并生成“p_form”,就可以将 %tones_in_octave 应用于列表以实际获取素数形式,所以不用担心。 好的@JamesWilkinson 很高兴我能帮上忙。您是否已经找到解决此问题的方法?如果答案有帮助,您可以考虑接受:) 问题 2:在大于三个 prime_form(0,2,4,5,7,9,11) 的集合中给出区间 [2,2,1,2,2,2,1 ]。这是正确的,但是 sintervals 使它们乱序到 [1,1,2,2,2,2,2]。这些数字可以转换的实际方法是,您可以从左或右的任何项目开始列出它们,但它们必须保持顺序。目标是左紧凑性,因此正确的顺序是 [1,2,2,1,2,2,2]。 [1,4,1,1,5] 是 [1,1,4,1,5],[2,1,2,4,2,1] 是 [2,1,2,1,2,4 ] 等等。我认为仅对间隔进行排序就可以在超过 3 个音高上起作用,这是对我的巨大疏忽。对不起>_>。 第一个问题的解决方案似乎是添加 "true_p_form = list(i % tones_in_octave for i in p_form)" "print('\nthis is the prime form!\n' .format(true_p_form))" 我不确定问题 2 的解决方案以上是关于如何在 Python 3.5.1 中使用 *args 在列表中进行计算的主要内容,如果未能解决你的问题,请参考以下文章
在 python 中使用 *args 区分不同维度的多变量函数