在遍历 RGB 颜色时,如何减少代码重复?

How can I reduce code duplication when iterating through RGB colors?

提问人:jonathan gabizon 提问时间:11/11/2023 最后编辑:mkrieger1jonathan gabizon 更新时间:11/12/2023 访问量:57

问:

我编写了一个脚本,以预设的增量遍历 3 个 RGB 值。

您会注意到六个循环的复制/粘贴,每个循环都有变化。

我想这六个部分可以组合成一个或两个父循环,通过一个子循环而不是 6 个递增。这有意义吗?

我的目标是减小此代码的大小。

代码如下:

color_numbers = [0,255]
increment_between_values = [64,64,63,64]

r_val=[]
g_val=[]
b_val=[]
rgb_values=[]#FORMAT: 255,255,255

def color_function():
    color_base_b = color_numbers[0]
    color_top_g = color_numbers[1]
    color_base_r = color_numbers[0]

    for x in range(len(increment_between_values)):
        r_val.append(color_numbers[0])
        g_val.append(color_numbers[1])
        color_base_b += increment_between_values[x]
        b_val.append(color_base_b)
        
    for x in range(len(increment_between_values)): 
        r_val.append(color_numbers[0])
        color_top_g -= increment_between_values[x]
        g_val.append(color_top_g)
        b_val.append(color_base_b)
            
    for x in range(len(increment_between_values)): 
        color_base_r += increment_between_values[x]
        r_val.append(color_base_r)
        g_val.append(color_top_g)
        b_val.append(color_base_b)
            
    for x in range(len(increment_between_values)): 
        r_val.append(color_base_r)
        g_val.append(color_top_g)
        color_base_b -= increment_between_values[x]
        b_val.append(color_base_b)
            
    for x in range(len(increment_between_values)): 
        r_val.append(color_base_r)
        color_top_g += increment_between_values[x]
        g_val.append(color_top_g)
        b_val.append(color_base_b)
            
    for x in range(len(increment_between_values)): 
        color_base_r -= increment_between_values[x]
        r_val.append(color_base_r)
        g_val.append(color_top_g)
        b_val.append(color_base_b)  
            
    for t in range(len(r_val)):
        print(str(r_val[t])+","+str(g_val[t])+","+str(b_val[t]))

color_function()

据我所知,每个循环的变化如下: 循环 1:初始值 循环 2:初始值 循环 3:向上递增 循环 4:最高值 循环 5:最高值 循环 6:递增

这六个步骤分别针对 R、G 和 B 进行偏移 例如:当 R 在循环 1 上时,G 在循环 3 上,B 在循环 5 上。 还有第二个循环,从 + 切换到 -(递增,递减)

我希望我已经清楚地解释了这一点 代码按原样工作,我只是想了解如何优化它。

python 循环 颜色 迭代 rgb

评论

1赞 roganjosh 11/11/2023
工作代码应发布在代码审查上。
0赞 Dean Van Greunen 11/11/2023
我建议您从 HSL 生成 RGB 代码,然后运行 HSL 并生成所有 HSL 值
0赞 Dean Van Greunen 11/11/2023
freecodecamp.org/news/generate-colors-in-javascript
1赞 Mark Setchell 11/12/2023
请说出你真正想要实现的目标,而不是说你想在某些步骤中迭代 6 次。您是否正在尝试减少颜色的数量?如何?为什么?

答:

0赞 chrslg 11/12/2023 #1

你可以利用这样一个事实,即只有一个 0,一个 255,另一个东西,如果它从 0 开始,则向上迭代,如果它从 255 开始,则向下迭代

def mycf():
    c=[0,0,0] # Starting point
    l=[0,64,128,192,255] # Possible values
    res=[] # Result
    for p0 in range(3): # Where is 0 in c 
        c[p0]=0
        for p255 in range(3): # Where in 255 in c
            if p255==p0: continue # Cannot be where is 0
            c[p255]=255
            pother=3-p0-p255 # Where is the other value (sum p0+p255+pother = 0+1+2 (in another order) = 3. So 3-p0-p255 is the pother)
            if c[pother]!=l[0]: l=l[::-1] # Iterates possible value from where we are. That is either 0 or 255. So if we are not at l[0], reverse it
            # The if is for clarity. In fact we could have started with l=[255,192,128,64,0] and reverted each times inconditionnaly
            for x in l[1:]:
                c[pother]=x
                res.append(list(c))
    return res

Itertools瑜伽

如果你真的想减少代码行数,我们可以用一些迭代工具做一个单行(但一大行)

import itertools
def listRgb(*p):
    return list(itertools.chain(*itertools.chain(*zip(itertools.islice(zip(*(itertools.permutations([0,255,x]) for x in p[1:])),0,None,2),itertools.islice(zip(*(itertools.permutations([0,255,x]) for x in p[-2::-1])),1,None,2)))))

listRgb(0,64,128,192,255)

解释:

itertools.permutations([0,255,x])是 0,255 和 x 的所有组合,以确定性顺序 (0,255,x)、(0,x,255)、(255,0,x)、...或者更准确地说,它是一个迭代器,遍历这些三元组(其中 6 个)

迭代器也是如此,因为 x 是 4 个值 64,128,192,255 中的任何一个。因此,如果我们展开这个“迭代器的迭代器”,我们将得到 4 个序列,每序列 6 个三元组序列。(itertools.permutations([0,255,x]) for x in p[1:])

因此,返回由每个序列的一个元素组成的四胞胎。所以三胞胎四胞胎的迭代器。其中 6 个。对应于 0、255 和 x 的每个可能仿真,以及三元组的每个 x 值。按 x 的升序排列。我们快到了。除了,我们希望它们在一半的时间里按降序排列。zip(*(itertools.permutations([0,255,x]) for x in p[1:]))

zip(*(itertools.permutations([0,255,x]) for x in p[-2::-1]))降序是同一回事吗

请注意,第一个跳过 0(这与上一次迭代的最后一个是多余的),而第二个跳过 255(同样)。

因此,我们对第一个序列(升序序列)中的一半感兴趣,对第二个序列中的一半感兴趣。

我们可以用 2 个切片之一islice

itertools.islice(zip(*(itertools.permutations([0,255,x]) for x in p[1:])),0,None,2)保持升序序列
和降序序列
itertools.islice(zip(*(itertools.permutations([0,255,x]) for x in p[-2::-1])),1,None,2)

我们可以用 zip 从这些半部分中挑选一个(这是一种从我们挑选序列的地方交替的方式)

zip(itertools.islice(zip(*(itertools.permutations([0,255,x]) for x in p[1:])),0,None,2),itertools.islice(zip(*(itertools.permutations([0,255,x]) for x in p[-2::-1])),1,None,2))

快到了。 由于我们在这里拥有的是 4 个三胞胎的成对(从最后一个 zip 开始);其中 3 个,即 4 个三胞胎的三元组(或者,准确地说,迭代器,如果我们迭代它,它会计算它。目前,我们还没有进行任何计算。它只是一个迭代器),我们需要连接结果。由于 itertools 的全部意义在于将事物保留为迭代器(在我们真正迭代事物之前不使用任何内存或 cpu),而不是将其转换为列表并将它们连接起来,因此我使用它将一个接一个地迭代 3 对中的每一对,以获得一个 6 个 uplet,chain

迭代器也是如此,迭代 6 个 4 个三元组。同样,我们只想迭代三元组。所以,再说一遍itertools.chain(*zip(itertools.islice(zip(*(itertools.permutations([0,255,x]) for x in p[1:])),0,None,2),itertools.islice(zip(*(itertools.permutations([0,255,x]) for x in p[-2::-1])),1,None,2)))chain

itertools.chain(*itertools.chain(*zip(itertools.islice(zip(*(itertools.permutations([0,255,x]) for x in p[1:])),0,None,2),itertools.islice(zip(*(itertools.permutations([0,255,x]) for x in p[-2::-1])),1,None,2))))是最终的迭代器。值为三元组的那个

事实上,它应该是我的函数返回的内容(而不是返回它)。因为列表会创建整个列表。也许你只是想一个接一个地枚举这些颜色,而没有必要将它们保留在内存中。我在我的函数中添加只是为了演示。但是你的问题是,所以没有必要建立一个列表。listlistiterate

例如

import itertools
def itRgb(*p):
    return itertools.chain(*itertools.chain(*zip(itertools.islice(zip(*(itertools.permutations([0,255,x]) for x in p[1:])),0,None,2),itertools.islice(zip(*(itertools.permutations([0,255,x]) for x in p[-2::-1])),1,None,2))))

for r,g,b in itRgb(*range(256)):
   print(r,g,b)

使用步骤 1 打印 1530 值,而无需生成这些值的列表

所以

tl;博士

您可以使用单行代码迭代颜色:

p=[0,64,128,192,255]
for r,g,b in itertools.chain(*itertools.chain(*zip(itertools.islice(zip(*(itertools.permutations([0,255,x]) for x in p[1:])),0,None,2),itertools.islice(zip(*(itertools.permutations([0,255,x]) for x in p[-2::-1])),1,None,2)))):
    print(r,g,b)

评论

1赞 Mark Ransom 11/12/2023
通常,较短代码的目标是使其更容易理解,而不是更难。
0赞 chrslg 11/12/2023
@MarkRansom我从来没有说过这是个好主意。我什至不认为这是一个好主意。但我不是那个要求“更短的代码”而不是“更容易理解的代码”的人:D
0赞 chrslg 11/12/2023
@MarkRansom,如果对我来说,我会使用已经执行此类任务的工具。例如,然后.不完全相同的确切值。但是,好吧,它确实迭代了RGB的彩虹。我什至非常确定在预定义的颜色图中已经有这样的颜色图。但这不是问题。m=LinearSegmentedColormap.from_list('m', [(0,2,0), (0,0,2),(2,0,0),(0,2,0)], N=10000)np.round(m(np.linspace(0.042,1,24))*255)[:,:3]matplotlib
0赞 Kelly Bundy 11/12/2023 #2

变量名称过长、具有误导性且使用不一致。而且按索引循环很糟糕。建议:

def color_function():
    r = b = color_numbers[0]
    g = color_numbers[1]

    def append():
        r_val.append(r)
        g_val.append(g)
        b_val.append(b)

    for inc in increment_between_values:
        b += inc
        append()
        
    # the other five loops similarly...

    for rgb in zip(r_val, g_val, b_val):
        print(*rgb, sep=",")

仍然是六个循环,但更短/更清晰,很容易看到发生了什么。