2017-01-03 2 views
0

I Indexwerte zurückgegeben werden soll von einer Funktion eine tuple eine Int enthält verwenden und eine Double. Die Funktion funktioniert gut Double Rückkehr, aber wenn ich einen tuple lesen bekomme ich diesen Fehler:Warum gibt eine Swift-Funktion mit .flatMap `Double`, aber nicht` tuple 'zurück?

'flatMap' produces '[SegmentOfResult.Iterator.Element]', not the expected contextual result type '(pitchClass: Int, frequency: Double)'

Ich bin verwirrt, warum .flatMap liefert Double aber nicht tuple.This link zu flatMap(_:) macht Sinn für arrays, aber ich kann keine Verbindung mit tuples sehen, die Licht auf dieses Problem wirft.

Was ist der beste Weg, dies zu lösen?

KLARSTELLUNG

Wie gewünscht

tuning ein String-Array mit Fraktionen wird und Dezimalzahlen

// test pitches: rational fractions and decimal numbers 
    let tuning = ["1/1", "200.0", "5/4", "500.0", "600.0", "700.0", "1000.0", "2/1"] 

scaleFrequencies eine Funktion, die beiden Nummerntypen und gibt Doppel verarbeitet ist (d frequency).

Ich habe Code-Initialisierung enthalten.

import UIKit 

class Tuner { 

     var tuning     = [String]() 
     let tonic: Double   = 261.626 // frequency of middle C 
     var index     = -1 
     let centsPerOctave: Double = 1200.0 // mandated by Scala tuning file format 
     let formalOctave: Double = 2.0  // Double for stretched-octave tunings 

init(tuning: [String]) { 
     self.tuning     = tuning 

     var notes: (pitchClass: Int, frequency: Double) 
     notes = tuning.flatMap(scaleFrequencies) 

    // print(frequency) 

    } 



func pitchClass(pitchClass: Int, _ frequency: Double) -> Int { 
    return pitchClass 
    } 

func frequency(pitchClass: Int, _ frequency: Double) -> Double { 
    return frequency 
    } 



func scaleFrequencies(s: String?) -> (Int, Double) { 

    index     += 1 
    var frequency: Double = 0.0   
    frequency    = processNumerics(numericString: numericString) 

    return (index, frequency) 
} 
+4

Was ist 'tuning'? Was sind "skalenFrequenzen"? Warum erwartest du 'tuning.flatMap (scaleFrecies)', um ein Tupel zu erzeugen, das man 'notes' zuordnen kann? – jtbandes

+0

Meinen Sie "Tuning" an Ihre Funktion übergeben, wie in 'scaleFrequencies (tuning)'? – jtbandes

+0

Jedes Element von 'tuning' wird wiederum an' scaleFrequencies.' übergeben. Von dort wird es an eine andere Funktion 'processNumerics' übergeben, wo Optionale ausgepackt werden (mit Guard und Null Coalescing dh keine erzwungene Unwrapping) - und 'frequency' wird berechnet und returned – Greg

Antwort

0

Die Funktion arbeitet zur Zeit in Ordnung Double Rückkehr und gibt bereits die Variable ich brauche, nämlich frequency. Es ist besser, es nicht zu ändern. Ich werde eine neue Klasse erstellen, um weitere Funktionen hinzuzufügen (z. B. Zuordnung von Frequenzen zu Tasten eines MIDI-Keyboards). Vielen Dank Rob und jtbandes für Ihre Eingabe.

EDIT

Es war sinnvoller, dieses Problem zu lösen, ohne das Problem Raum zu erweitern. Ich fand meine Lösung, sobald ich die Notwendigkeit in Frage gestellt hatte, ein Argument an die Funktion zurückzugeben, und fragte stattdessen, wie Daten von innerhalb der Funktion präsentiert werden könnten, wobei nur die verfügbaren Argumente verwendet würden. Die Lösung richtet sich ein verwandtes Problem, wie durch die accepted answer to another post identifiziert und optionale Werte abwickelt ohne Fehler Laufzeit verursacht, wie bei einigen anderen Stellen empfohlen (here, here und here.)

Werte werden mit optional Verkettungsausgepackt und kein Koaleszieren statt erzwungenes Auspacken. Zahlen, die nach gültig sind, werden in Frequenzen konvertiert und einem MIDI-Keyboard zugeordnet. Ungültige Stimmwerte und Notenzeichen, die mit einem ‘x’ (wie von these rules spezifiziert) markiert sind, erzeugen eine Frequenz von 0,0 Hz.

Die Lösung ermöglicht, dass optionale Skalenwerte, die von der Funktion gelesen werden, leichter mit Frequenzen verglichen werden können, die zurückgegeben werden, wenn andere Funktionen aufgerufen werden.

z.B.

Octave map 

    0 Optional("x") 
    0 : 0.0 
    1 Optional("35/32") 
    1 : 286.1534375 
    2 Optional("x") 
    2 : 0.0 
    3 Optional("x") 
    3 : 0.0 
    4 Optional("5/4") 
    4 : 327.0325 
    5 Optional("21/16") 
    5 : 343.384125 
    6 Optional("x") 
    6 : 0.0 
    7 Optional("3/2") 
    7 : 392.439 
    8 Optional("x") 
    8 : 0.0 
    9 Optional("x") 
    9 : 0.0 
    10 Optional("7/4") 
    10 : 457.8455 
    11 Optional("15/8") 
    11 : 490.54875 

Es hilft auch mit Lesefrequenzen in anderen Oktaven

MIDI map 

    Octave 0 
    0 0 0.0 
    1 1 8.942294921875 
    2 2 0.0 
    3 3 0.0 
    4 4 10.219765625 
    5 5 10.73075390625 
    6 6 0.0 
    7 7 12.26371875 
    8 8 0.0 
    9 9 0.0 
    10 10 14.307671875 
    11 11 15.3296484375 

    Octave 1 
    12 0 0.0 
    13 1 17.88458984375 
    14 2 0.0 
    15 3 0.0 
    16 4 20.43953125 
    17 5 21.4615078125 
    18 6 0.0 
    19 7 24.5274375 
    20 8 0.0 
    21 9 0.0 
    22 10 28.61534375 
    23 11 30.659296875 

    Octave 2 
    24 0 0.0 
    25 1 35.7691796875 
    26 2 0.0 

    etc 

    Octave 9 
    108 0 0.0 
    109 1 4578.455 
    110 2 0.0 
    111 3 0.0 
    112 4 5232.52 
    113 5 5494.146 
    114 6 0.0 
    115 7 6279.024 
    116 8 0.0 
    117 9 0.0 
    118 10 7325.528 
    119 11 7848.78 

    Octave 10 
    120 0 0.0 
    121 1 9156.91 
    122 2 0.0 
    123 3 0.0 
    124 4 10465.04 
    125 5 10988.292 
    126 6 0.0 

Entwickler von Musik-Anwendungen finden können dies nützlich, weil es zeigt, wie eine neu abgestimmte MIDI Karte erstellen. Die Lösung ermöglicht es, numerische Strings, die aus Bruchteilen und Dezimalzahlen bestehen, zu entpacken, die die Stimmung von Noten in Tonleitern spezifizieren, die über den Rahmen einer Standard-Musiktastatur hinausgehen. Seine Bedeutung wird auf niemanden verloren gehen, der this site besucht.

Hier ist der Code

Tuner.swift

import UIKit 

class Tuner   { 

    var tuning      = [String]() // .scl 
    var pitchClassFrequency   = Double()  // .scl 
    let centsPerOctave: Double  = 1200.0  // .scl mandated by Scala tuning file format 

    let formalOctave: Double  = 2.0   // .kbm/.scl Double for stretched-octave tunings 
    var octaveMap     = [Double]() // .kbm/.scl 
    var midiMap      = [Double]() // .kbm 

    let sizeOfMap     = 12   // .kbm 
    let firstMIDIKey    = 0    // .kbm 
    let lastMIDIKey     = 127   // .kbm 

    let referenceMIDIKey   = 60   // .kbm 
    let referenceFrequency: Double = 261.626  // .kbm frequency of middle C 

    var indexMIDIKeys    = Int() 
    var indexOctaveKeys    = Int() 
    var currentKeyOctave   = Int() 
    var index: Int     = 0 


init(tuning: [String]) { 
    self.tuning     = tuning 

// SCL file format - create frequency map of notes for one octave 
    print("Octave map") 
    print("") 
    let _       = tuning.flatMap(scaleToFrequencies) 

// KBM file format - create frequency map of MIDI keys 0-127 
    print("") 
    print("MIDI map") 
    let _       = createMIDIMap() 

    } 


func createMIDIMap()           { 

    indexOctaveKeys    = firstMIDIKey // set indexOctaveKeys to pitchClass 0 
    currentKeyOctave   = firstMIDIKey // set currentOctave to octave 0 

    for indexMIDIKeys in firstMIDIKey...lastMIDIKey { 
     let indexOctaveKeys  =  indexMIDIKeys % sizeOfMap 

     currentKeyOctave  = Int((indexMIDIKeys)/sizeOfMap) 
     let frequency   = octaveMap[indexOctaveKeys] * 2**Double(currentKeyOctave) 

     //  midiMap[i]    = octaveMap[indexMIDIKeys] * 2**Double(currentKeyOctave) 
     if indexOctaveKeys == 0 { 
      print("") 
      print("Octave \(currentKeyOctave)") 
     } 
     print(indexMIDIKeys, indexOctaveKeys, frequency) 
     } 
    } 


func scaleToFrequencies(s: String?)        { 

    var frequency: Double  = 0.0 

    // first process non-numerics. 
    let numericString   = zapAllButNumbersSlashDotAndX(s: s) 

    print(index, numericString as Any)  // eavesdrop on String? 

    // then process numerics. 
    frequency     = (processNumericsAndMap(numericString: numericString))/Double(2)**Double(referenceMIDIKey/sizeOfMap) 
    octaveMap.append(frequency) 
    print(index,":",frequency * 2**Double(referenceMIDIKey/sizeOfMap)) 
    index += 1 
    } 


func processNumericsAndMap(numericString: String?) -> Double { 
    guard let slashToken = ((numericString?.contains("/")) ?? nil), 
     let dotToken  = ((numericString?.contains(".")) ?? nil), 
     let xToken   = ((numericString?.contains("x")) ?? nil), 
     slashToken   == false, 
     dotToken   == true, 
     xToken    == false 
     else { 
      guard let dotToken = ((numericString?.contains(".")) ?? nil), 
       let xToken  = ((numericString?.contains("x")) ?? nil), 
       dotToken  == false, 
       xToken   == false 
       else { 
        guard let xToken = ((numericString?.contains("x")) ?? nil), 
         xToken   == false 
         else { 
          // then it must be mapping. 
          let frequency = 0.0 
          //      print("[x] \(frequency) Hz") 
          return frequency 
        } 
        // then process integer. 
        let frequency = processInteger(s: numericString) 
        return frequency 
      } 
      // then process fractional. 
      let frequency   = processFractional(s: numericString) 
      return frequency 
     } 
    // process decimal. 
    let frequency     = processDecimal(s: numericString) 
    return frequency 
    } 


func processFractional(s: String?) -> Double     { 
    let parts = s?.components(separatedBy: "/") 
    guard parts?.count  == 2, 
     let numerator  = Double((parts?[0])?.digits ?? "failNumerator"), 
     let dividend  = Double((parts?[1])?.digits ?? "failDenominator"), 
     dividend   != 0 
     else { 
      let frequency = 0.0 
      print("invalid ratio: frequency now being set to \(frequency) Hz") 
      return frequency 
     } 
    let frequency   = referenceFrequency * (numerator/dividend) 
    return frequency 
    } 


func processDecimal(s: String?) -> Double      { 

    let parts = s?.components(separatedBy: ".") 
    guard parts?.count  == 2, 
     let intervalValue = Double(s ?? "failInterval"), 
     let _    = Double((parts?[0])?.digits ?? "failDecimal") 
     else { 
      let frequency = 0.0 
      print("invalid cents value: frequency now being forced to \(frequency) Hz ") 
      return frequency 
     } 
    let power    = intervalValue/centsPerOctave      // value with explicit remainder 
    let frequency   = referenceFrequency * (formalOctave**power) 
    return frequency 
    } 


func processInteger(s: String?) -> Double      { 
    let frequency   = 0.0 
    print("not cents, not ratio : frequency now being set to \(frequency) Hz ") 
    return frequency 
    } 


func zapAllButNumbersSlashDotAndX(s: String?) -> String?  { 

    var mixedString = s 
    if mixedString != nil { 
     mixedString = mixedString! 
     } 
     guard var _ = mixedString?.contains("/"), 
     var _ = mixedString?.contains(".") 
     else { 
      let numberToken = mixedString 
      return numberToken 
      } 
      guard let xToken = mixedString?.contains("x"), 
       xToken == false 
       else { 
        let xToken = "x" 
        return xToken 
     } 
    let notNumberCharacters = NSCharacterSet.decimalDigits.inverted 
    let numericString = s?.trimmingCharacters(in: notNumberCharacters) ?? "orElse" 
    return numericString.stringByRemovingWhitespaces 
    } 

} 


extension String { 
var stringByRemovingWhitespaces: String { 
    return components(separatedBy: .whitespaces).joined(separator: "") 
    } 
} 


extension String { 

var digits: String { 
    return components(separatedBy: CharacterSet.decimalDigits.inverted).joined() 
    } 
} 


precedencegroup Exponentiative { 

    associativity: left 
    higherThan: MultiplicationPrecedence 

} 


infix operator ** : Exponentiative 


func ** (num: Double, power: Double) -> Double      { 
return pow(num, power) 
} 


func pitchClass(pitchClass: Int, _ frequency: Double) -> Int  { 
    return pitchClass 
} 


func frequency(pitchClass: Int, _ frequency: Double) -> Double  { 
    return frequency 
} 

ViewController.swift

import UIKit 

class ViewController: UIViewController { 

// Hexany: 6-note scale of Erv Wilson 

     let tuning = ["x", "35/32", "x", "x", "5/4", "21/16", "x", "3/2", "x", "x", "7/4", "15/8"] 


// Diatonic scale: rational fractions 
//  let tuning = [ "1/1", "9/8", "5/4", "4/3", "3/2", "27/16", "15/8", "2/1"] 

// Mohajira: rational fractions 
// let tuning = [ "21/20", "9/8", "6/5", "49/40", "4/3", "7/5", "3/2", "8/5", "49/30", "9/5", "11/6", "2/1"] 

// Diatonic scale: 12-tET 
// let tuning = [ "0.0", "200.0", "400.0", "500", "700.0", "900.0", "1100.0", "1200.0"] 


override func viewDidLoad() { 
    super.viewDidLoad() 

    _ = Tuner(tuning: tuning) 


    } 
} 
+0

Die ursprüngliche Antwort wurde möglicherweise abgelehnt, weil es unvollständig war. Hoffentlich geht mein Edit darauf ein. – Greg

Verwandte Themen