>

아래에 두 개의 스 니펫이 있습니다. 첫 번째 코드는 피아노의 모든 키를 출력하고 두 번째 코드는 시작 음표가 표시되면 스케일을 표시합니다. 피아노가 어떻게 구성되어 있는지 모르는 사람들을 위해 피아노 . 기본적으로 각 메모를 순서대로 배열하고 싶습니다. 보시다시피 검은 색 노트에는 b 라는 두 가지 이름이 있습니다.  그것과 # 와 하나 . 이 두 이름은 동일하지만 b 와 함께 사용하고 있습니다. .

이미지와 같은이 배열을 만들어야합니다 :

[ "A0", "Bb0", "B0", "C1", "Db1", "D1", "Eb1", "E1", "F1", "Gb1", "G1", "Ab1", "A1", "Bb1", "B1".............., "B7", "C8"]

또는 아래 jsfiddle의 콘솔 출력을 확인하십시오.

<시간>

jsfiddle

const KEYS_NORMAL =  ["C", "Db", "D", "Eb", "E", "F", "Gb", "G", "Ab", "A", "Bb", "B"];
let keys = KEYS_NORMAL.slice(-3);
keys = keys.map( k => k + '0');
for(let octave = 1; octave < 8; octave++){
  for(let key of KEYS_NORMAL){
    keys.push(key + octave)
  }
}
keys.push('C8')
keys.forEach(k => console.log(k));
// I'm actually doing some work with those notes, So ultimately this is one more loop.

<시간> 두 번째 알고리즘은 음표가 주어진 스케일을 반환합니다. 스케일을 모르는 사용자는 음표에서 시작하며 아래 알고리즘으로 제공됩니다.

주요 : 2-2-1-2-2-2-1

미성년 : 2-1-2-2-1-2-2

우리는 m 때문에 메이저와 마이너를 구별 할 수있다  이름에 추가됩니다 (예 : A ).   Am 반면 주요  미미합니다.

여기서 1은 위의 한 쪽 음표를, 2는 위의 음표 2 개를 의미합니다 (검정색 키도 음표입니다).

여기 jsfiddle

코드 :

majorSemiTones = [2,2,1,2,2,2];
 minorSemiTones = [2,1,2,2,1,2];
 KEYS = ["A", "Bb", "B", "C", "Db", "D", "Eb", "E", "F", "Gb", "G", "Ab"];
 KEYS_NORMALIZED = ["C", "Db", "D", "Eb", "E", "F", "Gb", "G", "Ab", "A", "Bb", "B"];
 function getScale(scaleName){
        let tonic = scaleName.replace("m", "");
        let keys = [];
        let minor = false;
        let scaleIndex;
        let intervals = this.majorSemiTones;
        if(scaleName.indexOf("m") > -1){
            minor = true;
            intervals = this.minorSemiTones;
        }
        scaleIndex = KEYS.indexOf(tonic);// starts on the tonic
        for(let i = 0; i < 7; i++){
            keys.push(KEYS[scaleIndex]);
            if( scaleIndex + intervals[i] > KEYS.length - 1 ){
                scaleIndex = scaleIndex + intervals[i] - KEYS.length;
            }else{
                scaleIndex += intervals[i];
            }
        }
        return keys;
    }

<시간>

이 리뷰에서 얻고 자하는 것은 코드를 향상시키는 방법입니다. 두 번째 스 니펫은 불필요하게 복잡하고 읽을 수없는 것처럼 보이지만 첫 번째 스 니펫은 비효율적이며 일부 기능적 js의 이점을 얻을 수있는 것 같습니다.


  • 답변 # 1

    이 질문에 typescript 태그를 추가 했으므로 키보드와 스케일 표현을 수업으로 만들면 큰 이점을 얻을 수 있습니다.

    <시간>

    키보드 레이아웃이 다르기 때문에 표준 키보드 구성을 기반으로 키보드를 만드는 기능을 일반화하는 것이 합리적입니까?

    키를 빌드하는 현재 로직은 기본적으로 표준 88 키 키보드에 대해 하드 코딩 된 것 같습니다. 키가 채워지는 메인 루프 이전 (옥타브 0 키 설정)과 메인 루프 이후 (옥타브 8 키 설정)에 이상한 작업 혼합이 발생합니다. 이 로직이 현재처럼 분리되지 않도록 루프 내에서 모든 키 값을 설정하려고 노력합니다.

    <시간>

    두 가지 논리에서, 입력 된 값을 기준으로 배열 작업을위한 적절한 시작 위치에 도달하기 위해 12 개 음의 배열을 "회전"하는 것을 고려할 수 있습니다. 이렇게하면 배열에서 경계 조건을 찾을 필요가 없어 (즉, 인덱스 0으로 돌아 가야 할 때) 배열 상호 작용 논리를 훨씬 깨끗하게 만들 수 있습니다.

    <시간>

    데이터 입력과 관련하여 코드에 취약성이 있습니다. 입력 데이터에서 대소 문자를 검증하거나 처리하는 것은 실제로 없습니다 (예 : a 와 같은 경우 ABm getScale() 에 전달 ?).

    이와 같은 코드 :

    if(scaleName.indexOf("m") > -1){
    
    

    다음과 같이 더 구체적으로 만드는 것이 좋습니다 :

    if(scaleName.indexOf("m") === 1) {
    
    
    m 에 인덱스 값이 하나만 있으므로

     항상 만나야합니다.

    또는 /^([A-G]b?)(m?)$/ 와 같은 정규 표현식을 고려할 것입니다.  입력 문자열의 유효성을 검사하고 문자열의 구성 요소를 한 번에 캡처 할 수 있습니다.

    <시간>

    나는 KEYS 의 필요성을 이해하지 못한다  그리고 KEYS_NORMALIZED  스케일 코드에서. 이것은 동일한 배열 회전이며 특정 입력 스케일에 따라 배열을 회전하거나 변환해야한다는 것을 고려할 때 왜 같은 값을 갖는 두 배열이 있습니까?

    <시간>

    일부 변수 이름이 혼란스러워 보입니다. 예를 들면 다음과 같습니다.

    "피아노"코드에서 KEYS  상수는 notes 라고하는 것이 좋습니다.  또는 keyNotes  이 배열은 키보드의 실제 "키"를 나타내지 않기 때문에 유사합니다.

    와이즈 비즈 getScale()  배열은 스케일의 키 또는 키보드의 키를 다루지 않고 스케일의 노트를 처리하므로 keys  더 나은 이름 일 수도 있습니다.

    와이즈 비즈  Wyzwyz  변수 이름은 notes 로 지정하는 것이 좋습니다. .

    <시간>

    모두 함께 넣으면 다음과 같은 결과가 나올 수 있습니다 :

    getScale()
    
    

    이 코드가 더 있습니까? 예. 그러나이 코드는 재사용 성이 높고 덜 취약하며 현재 코드 예제와 같이 전역 네임 스페이스를 오염시키지 않습니다. 모든 논리는 클래스 내에 캡슐화되어 있습니다.

  • 답변 # 2

    검토

    (1) 알고리즘

    옥타브와 음표 이름은 완전히 독립적이므로, 정보를 저장하는 클래스를 사용하여 보일러 플레이트 연결을 완화하고 재사용 성을 최적화 할 수 있습니다. 음표의 음정 및 피치를 결정하기 위해 추가적인 복잡성이 필요합니다. 대체 솔루션에이를위한 알고리즘을 추가했습니다.

    와이즈 비즈 와이즈 비즈
    major/minorSemiTones
    
    

    (2) 알고리즘

    스케일을 식별하기 위해 간격 패턴을 사용하는 아이디어가 좋습니다.

    와이즈 비즈 와이즈 비즈

    하지만 알고리즘으로 하드 코딩 할 때 확장 성과 모듈성을위한 공간이 충분하지 않습니다.

    와이즈 비즈 와이즈 비즈 대체

    예상 결과 제공 :

    와이즈 비즈 와이즈 비즈      두 번째 알고리즘은 음표가 주어진 스케일을 반환합니다. 스케일을 모르는 사용자는 음표에서 시작하며 아래 알고리즘으로 제공됩니다.

    major/minroSemiToneIntervals
    
    

    입력와 스케일간격 패턴이 주어지면스케일을 얻을 수있는 작은 API를 만들었습니다.

    브라우저 콘솔로 출력되는 바이올린

    class Keyboard {
         static readonly standardKeyboardConfig = {
             49: {
                 startKey: 'C',
                 startOctave: 0,
                 octaveBoundary: 'C'
             }
             61: {
                 startKey: 'C',
                 startOctave: 0,
                 octaveBoundary: 'C'
             },
             76: {
                 startKey: 'E',
                 startOctave: 0,
                 octaveBoundary: 'C'
             },
             88: {
                 startKey: 'A',
                 startOctave: 0,
                 octaveBoundary: 'C'
             }
         }
         static readonly defaultKeyCount = 88;
         static readonly keyNotes = ["C", "Db", "D", "Eb", "E", "F",
             "Gb", "G", "Ab", "A", "Bb", "B"];
         readonly keys: string[];
         constructor(keyCount: number) {
             keyCount = keyCount || Keyboard.defaultKeyCount;
             if (keyCount in Keyboard.standardKeyboardConfig === false) {
                 throw new RangeError('Invalid keyboard specified');
             }
             let config = Keyboard.standardKeyboardConfig[keyCount]; 
             let startKey = config.startKey;
             // first rotate the array of notes if needed
             let keyNotes = Keyboard.keyNotes;
             if(startKey !== keyNotes[0]) {
                 let index = keyNotes.indexOf(startKey);
                 keyNotes = keyNotes.slice(index, keyNotes.length)
                                  .concat(keyNotes.slice(0, index));
             }
             // now build out keys with octave values
             let octave = config.startOctave;
             let octaveLength = keyNotes.length;
             let octaveBoundary = config.octaveBoundary;
             for (i = 0; i < keyCount; i++) {
                 let noteIndex = i % octaveLength;
                 let note = keyNotes[noteIndex];
                 if (i > 0 && note === octaveBoundary) {
                     octave++;
                 }
                 this.keys.push(note + octave);
             }
         }
    }
    // usage for default keyboard (a piano)
    let piano = new Keyboard();
    let keys = piano.keys;
    class Scale {
        static readonly majorSemiToneIntervals = [2,2,1,2,2,2];
        static readonly minorSemiToneIntervals = [2,1,2,2,1,2];
        static readonly regex = /^([A-G]b?)(m?)$/;
        static readonly allNotes = ["A", "Bb", "B", "C", "Db", "D", "Eb",
            "E", "F", "Gb", "G", "Ab"];
        readonly name: string;
        readonly tonic: string;
        readonly isMinor: boolean = false;
        readonly notes: string[];
        constructor (scaleName: string) {
            // validate scale name and capture components
            let match = Scale.regex.match(scaleName);
            if(match === null) {
                throw new RangeError('Invalid scale name.')
            };           
            this.name = match[0];
            this.tonic = match[1];
            let intervals = Scale.majorSemiToneIntervals;
            if(match[2].length === 1) {
                this.isMinor = true;
                intervals = Scale.minorSemiToneIntervals;
            }
            // rotate the allNotes array
            let notes = Scale.allNotes;
            let index = notes.indexOf(this.tonic);
            if (index !== 0) {
                let notes = notes.slice(index, notes.length)
                                 .concat(notes.slice(0, index));
            }
            // build notes for scale based on intervals;
            this.notes.push(notes[0]);
            noteIndex = 0;
            intervalIndex = 0;
            while (intervalIndex < intervals.length) {
                this.notes.push(notes[noteIndex]);
                noteIndex = noteIndex + intervals[intervalIndex];
                intervalIndex++;
            }
        }   
    }
    // usage
    let scale = new Scale('C');
    let notesInScale = scale.notes;        
    
    

    이로 인해 음표 이름을 사용하여 다음과 같은 음계가 만들어집니다. enharmonic 이름을 사용하지 않으려면

    를 대체해야합니다.
     
    const KEYS_NORMAL =  ["C", "Db", "D", "Eb", "E", "F", "Gb", "G", "Ab", "A", "Bb", "B"];
    let keys = KEYS_NORMAL.slice(-3);
    keys = keys.map( k => k + '0');
    와 함께
    . 또는-귀하의 경우-평평한 변경된 노트 만 원한다면 
    class Note {
        constructor(degree, pitch) {
            this.degree = degree;
            this.pitch = pitch;
        }
        get Octave() {
            return this.modulo(this.pitch);
        }
        set Octave(n) {
            this.pitch = n * 12 + this.modulo(this.pitch);
        }
        modulo(n) {
            return (n % 12 + 12) % 12;
        }
        ..
    }
    를 사용하십시오.
    .

    
    

    결과

    스케일 기능별

    majorSemiTones = [2,2,1,2,2,2];
    minorSemiTones = [2,1,2,2,1,2];
    
    

    고조파

    
    

    내림차순 내림차순(원하는 방법)

    if(scaleName.indexOf("m") > -1){
                minor = true;
                intervals = this.minorSemiTones;
            }
    
    

    오름차순 조화로운

    So I have to create this array (like the image):

    
    [ "A0", "Bb0", "B0", "C1", "Db1", "D1", "Eb1", "E1", "F1", "Gb1", "G1", "Ab1", "A1", "Bb1", "B1".............., "B7", "C8"]
    

  • 이전 java - SHA-1 해시의 처음 몇 비트의 충돌 찾기
  • 다음 javascript - 간단한 Nodejs 사용자 관리 시스템