How to access indexes backwards within a list?

Asked

Viewed 669 times

2

I’m taking the notes that make up the triads of the major chords. For this, I created a list of all possible notes, and according to the chord typed I see the list looking for the first largest, third largest and fifth just, with the following calculation:

notas = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B']
triade = []
acorde = str(input('Acorde desejado: '))
if len(acorde) == 1:  # se for um acorde maior sem sétima
    for i, nota in enumerate(notas):
        if nota == acorde:
            triade.append(nota)  # 1º maior
        elif i == notas.index(acorde) + 4:
            triade.append(nota)  # 3º maior
        elif i == notas.index(acorde) + 7:
            triade.append(nota)  # 5º justa

print(triade)

The problem is that if I catch any chords from F, The calculation will go over the list limit and it will be a mistake. To correct this, I could simply repeat the values within it again that would work, but I found a curiosity; I can reorder the list from the chord typed?

Ex: Desired chord: A

notes = ['A', 'A#', 'B', 'C', 'C'#'...]

I could also for a if should you arrive at i == 11, to restart the count, or is there a calculation that can do this without a conditional?

4 answers

3


If your intention is to cycle the indexes of a list on the right, that is to do that when the searched index exceeds the length of the list back to the beginning, just get the rest of the entire division between the index and the length of the list using operator %(Operador Resto):

notas = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B']
triade = []
acorde = input('Acorde desejado: ').upper() #o retorno input já uma string
if len(acorde) == 1:  # se for um acorde maior sem sétima
    for i, nota in enumerate(notas):
        if nota == acorde:
            triade.append(nota)  # 1º maior
        elif i == (notas.index(acorde) + 4) % len(notas):
            triade.append(nota)  # 3º maior
        elif i == (notas.index(acorde) + 7) % len(notas):
            triade.append(nota)  # 5º justa

print(triade)

Test in Repl.it

3

If you only want to get 3 specific elements from the list of notes, you don’t need to make one loop throughout the list. Just take the index of the entered element and from it calculate the others, and then you create another list only with these indexes:

if len(acorde) == 1:
    try:
        i = notas.index(acorde)
        triade = [ notas[i], notas[(i + 4) % len(notas)], notas[(i + 7) % len(notas)] ]
    except ValueError:
        print(f'{acorde} não é um acorde válido')

The detail is that according to the documentation the method index spear one ValueError if the item does not exist in the list, then I have included this.

Another detail is that you just need to call index once, and from the result of it we calculate the other indexes (4 and 7 forward positions, using the operator % to return to the top of the list if applicable, as per another answer already explained).

So it gets better because every call from index you need to go through the list again, until you find the element in question (that is, the way you had done it, the list is covered several times, without need). As the element is the same throughout the loop and the list is not modified, just call index once. And since I just want specific positions from this index, you don’t have to go through the entire list until you find those positions: take them directly and that’s it.

By scrolling through the list several times without need, you are creating a variation of the call Shlemiel the Painter’s Algorithm.


The difference is that not always the order of the list triade will be the same as your code. For example, if the chord typed is 'A', to another answer has as a result the list ['C#', 'E', 'A'], already the code above has as a result the list ['A', 'C#', 'E'].

That’s because, like the A is closest to the end of the list and the following positions are at the beginning, the loop finds the C# and the E before.

If you want to maintain the same order as yours loop original, just sort the indexes before getting the list:

if len(acorde) == 1:
    try:
        i = notas.index(acorde)
        indices = sorted([ i, (i + 4) % len(notas), (i + 7) % len(notas) ])
        triade = list(map(lambda idx: notas[idx], indices))
    except ValueError:
        print(f'{acorde} não é um acorde válido')

First I use sorted to sort the indexes (the chord position, and 4 and 7 forward positions). Then I mapped each position to the respective element of the note list.

Instead of using map, another way to create the list triade from the indexes is to use a comprehensilist on, much more succinct and pythonic:

if len(acorde) == 1:
    try:
        i = notas.index(acorde)
        indices = sorted([ i, (i + 4) % len(notas), (i + 7) % len(notas) ])
        triade = [ notas[idx] for idx in indices ] # usar list comprehension em vez de list(map(etc...))
    except ValueError:
        print(f'{acorde} não é um acorde válido')

2

I thought of a very simple solution, consistent with the creation of a new list, with the reordering of the notes from the chord informed by the user, through the splitting of the original list:

notas = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B']
triade = []
acorde = str(input('Acorde desejado: '))
if len(acorde) == 1:  # se for um acorde maior sem sétima
    notas_ordenadas = notas[notas.index(acorde):] + notas[:notas.index(acorde)] # reordena as notas a partir do acorde escolhido
    triade = [notas_ordenadas[0], notas_ordenadas[4], notas_ordenadas[7]]

print(triade)

1

At the beginning of the question, we were given a lista, call for notas, that is to say...

notas = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B']

This list is nothing more than the chromatic scale of . In this situation we don’t have modos (mode major or minor).

Of course, on this list, in addition to the natural notes (C, D, E, F, G, A, B), we also have the bumpy notes (C#, D#, F#, G#, A#), that we can also call banknotes sustenizadas.

These notes, they’re not inside the list by chance. Yeah, they’re also podem formar chords. In this case, it is the sharp chords.

In the case of chord formation, there are several guys. For agility purposes, in this post, I will focus only on the chord Greater. For - in its most basic form - it is formed by only three notes and two intervals.

As notes evening:

  1. Tonic: 1st degree of scale;
  2. By means of: 3rd degree of scale;
  3. Dominant: 5th degree of scale.

And the breaks will be:

  1. Tuesday: 4 semitones between tonic and upon;
  2. Thursday: 7 semitones between tonic and dominant.

Observing: Intervals shall always be counted from tonic.

Adopting the tonic as being the bass of the chord, that is, positioning the tonic as the most serious note of the chord (1st note of the chord), we can implement the following script.

notas = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B']

while (baixo := input('Baixo do acorde desejado: ').upper()) not in notas:
    print('Valor INVÁLIDO!')

triade = [notas[(notas.index(baixo) + i) % 12] for i in (0, 4, 7)]

print(f'{baixo}M:', *triade)

This script requests the bass of the desired chord. Then it checks whether the bottom typed by the user is, in fact, valid, that is, checks whether the bottom typed by the user, in fact, is in the list notes. While the entered value is invalid, the script will display an error message and prompt the chord bass again. If the entered value is valid, the program will advance its execution.

Next time, the script will mount the desired chord with the help of list comprehension.

Note that in this step the block for go through the tuple (0, 4, 7). This tuple is formed by the size of the respective ranges.

The 0, is the null range. The value 4 corresponds to the range of tuesday and the value 7, corresponds to the range of just fifth.

For each loop iteration the respective desired note is mounted, obtaining with the following code:

notas[(notas.index(baixo) + i) % 12]

The function of this code is basically to identify the note, whose bottom index is in the list notes. And, for this, it is calculated the REMNANT of the actual division between the sum of (notes.index(low) with its said interval i and the value 12. The value 12 matches the list size notes.

Once mounted the chord it will be displayed using the following format:

print(f'{baixo}M:', *triade)

This format displays the name of bass together with the designation of major (M), followed by two points ":" and followed by the unpacking of the triad (*Triad).

Testing the code:

When executing the code and entering the value...

A

...and press Enter, we will get as a result:

AM: A C# E

In this way, we received as a result the name of the chord, along with its way, together with the notes of the abovementioned agreement.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.