Swift 4 - Испытание, пытаясь использовать Generics для уменьшения формульного кода - PullRequest
0 голосов
/ 03 октября 2018

Я начал сегодня утром с простой задачи тестирования некоторых математических функций на SCNVector3s и в итоге увяз в куче протоколов и обобщений.Я пытался реализовать концептуально простое расширение, которое позволяет создавать случайные SCNVector3s, что особенно полезно для генеративного тестирования.

Следующий скрипт swift действительно позволяет легко генерировать случайные векторы.Задача состоит в том, чтобы уменьшить репликацию кода, присущую реализации различных комбинаций открытых и закрытых диапазонов для трех измерений (8 функций).Как ни старайся, я не могу понять, как использовать универсальные функции и / или протоколы, чтобы свести эту функциональность в единый метод, который, на мой взгляд в C ++, кажется тривиальной задачей.Увы, я узнал, что нельзя просто думать об обобщениях и протоколах как об эквивалентах шаблонов.

Текст всех функций в точности эквивалентен, за исключением типа аргумента - что делает его слишком болезненным для меняповторить код.Я просто не могу заставить себя сделать это, хотя это положило бы конец моей трагической саге несколько часов назад.

Любая помощь в решении проблемы будет принята с благодарностью.

#!/usr/bin/env swift

import SceneKit

extension SCNVector3 {
    public static func random(_ range: ClosedRange<CGFloat>) -> SCNVector3 {
        return SCNVector3(CGFloat.random(in: range),
                          CGFloat.random(in: range),
                          CGFloat.random(in: range))
    }

    public static func random(_ range: Range<CGFloat>) -> SCNVector3 {
        return SCNVector3(CGFloat.random(in: range),
                          CGFloat.random(in: range),
                          CGFloat.random(in: range))
    }

    public static func random(_ xrange: ClosedRange<CGFloat>,
                              _ yrange: ClosedRange<CGFloat>,
                              _ zrange: ClosedRange<CGFloat>) -> SCNVector3 {
        return SCNVector3(CGFloat.random(in: xrange),
                          CGFloat.random(in: yrange),
                          CGFloat.random(in: zrange))
    }
}

for _ in 0...5 {
    print(SCNVector3.random(0...1))
}

for _ in 0...5 {
    print(SCNVector3.random(0..<1))
}

for _ in 0...5 {
    print(SCNVector3.random(0...1, 0...10, 0...100))
}

SCNVector3(x: 0.30337554055051663, y: 0.3815295391899972, z: 0.4500107875772762)
SCNVector3(x: 0.8292976915969825, y: 0.09817659394351774, z: 0.9805310965643402)
SCNVector3(x: 0.10140452934182276, y: 0.13700006723273783, z: 0.003407601812085548)
SCNVector3(x: 0.2794740490735984, y: 0.8092883659638909, z: 0.7611573009648945)
SCNVector3(x: 0.5245643085628658, y: 0.08307239252197174, z: 0.4335406226121913)
SCNVector3(x: 0.43781151814220054, y: 0.061963776367431, z: 0.18073354555266563)
SCNVector3(x: 0.10427323503781749, y: 0.8816323284041111, z: 0.7307715923086391)
SCNVector3(x: 0.36332454445518303, y: 0.7568856199566694, z: 0.43190825321532156)
SCNVector3(x: 0.8236386316508026, y: 0.8079968534291148, z: 0.3294130964530748)
SCNVector3(x: 0.038760425835524304, y: 0.8453005937068554, z: 0.11379975436886769)
SCNVector3(x: 0.9980685456027362, y: 0.6776965236898836, z: 0.6814096250296368)
SCNVector3(x: 0.01414002018834537, y: 0.1922579292321731, z: 0.5310331022793705)
SCNVector3(x: 0.6720908484435982, y: 6.815521332533848, z: 47.73040146101302)
SCNVector3(x: 0.05912412792498123, y: 7.709586490036736, z: 87.70901825047801)
SCNVector3(x: 0.9603565579370552, y: 9.627783890657632, z: 83.3390228893866)
SCNVector3(x: 0.4312469801270884, y: 1.0603895571013555, z: 73.97981933311189)
SCNVector3(x: 0.8079217337794122, y: 7.901726750285889, z: 83.322147654367)
SCNVector3(x: 0.7795445386815117, y: 6.845539611004492, z: 92.24684042413436)

1 Ответ

0 голосов
/ 03 октября 2018

Вы можете добавить протокол, назовем его Randomizable, который объединяет Range и ClosedRange под одним зонтиком, это устранит дублирование (ууу :))

public protocol Randomizable {
    associatedtype Value

    func random() -> Value
}

extension Range: Randomizable where Bound == CGFloat {
    public typealias Value = CGFloat

    public func random() -> CGFloat {
        return CGFloat.random(in: self)
    }
}

extension ClosedRange: Randomizable where Bound == CGFloat {
    public typealias Value = CGFloat

    public func random() -> CGFloat {
        return CGFloat.random(in: self)
    }
}

extension SCNVector3 {
    public static func random<R: Randomizable>(_ source: R) -> SCNVector3 where R.Value == CGFloat {
        return SCNVector3(source.random(),
                          source.random(),
                          source.random())
    }

    public static func random<R1: Randomizable, R2: Randomizable, R3: Randomizable>(
        _ xsource: R1,
        _ ysource: R2,
        _ zsource: R3) -> SCNVector3 where R1.Value == CGFloat, R2.Value == CGFloat, R3.Value == CGFloat {
        return SCNVector3(xsource.random(),
                          ysource.random(),
                          zsource.random())
    }
}

Какпримечание: поскольку у Randomizable есть связанные типы, домен, в котором может использоваться этот протокол, ограничен, вы можете обойти это, удалив связанный с Value тип и жестко закодировав его в CGFloat.Хотя это сделает протокол менее гибким.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...