【问题标题】:How to implement a ROT13 function in Swift?如何在 Swift 中实现 ROT13 函数?
【发布时间】:2016-06-10 20:10:33
【问题描述】:

我想创建一个函数,它接收一个字符串并返回一个字符串,并将一个字母替换为字母表中的 13 个字母(ROT13)。我找到了很多例子,不幸的是,由于各种错误,我无法让它发挥作用。比如这个:

var key = [String:String]() // EDITED
let uppercase = Array(arrayLiteral: "ABCDEFGHIJKLMNOPQRSTUVWXYZ")
let lowercase = Array(arrayLiteral: "abcdefghijklmnopqrstuvwxyz")
for i in 0 ..< 26 {
    key[uppercase[i]] = uppercase[(i + 13) % 26]
    key[lowercase[i]] = lowercase[(i + 13) % 26]
}

func rot13(s: String) -> String {
    return String(map(s, { key[$0] ?? $0 }))
}

【问题讨论】:

  • 为什么不把键数组变成字符串数组?
  • 你正试图用$0key中取出一个元素,这是一个Character,而不是Int
  • 谢谢。我现在将键设为字符串数组。现在字符串seq有问题:“extraneous argument label 'seq:' in call”
  • 我已经扩展了我的答案以将其推广到rotN,这使得rot13rot47rot5rot13rot5 的组合的实现变得简单。

标签: xcode string swift rot13


【解决方案1】:

实际上,您最初映射Characters 的方法很好:

var key = [Character: Character]()

但是这两个数组必须是Characters的数组:

let uppercase = Array("ABCDEFGHIJKLMNOPQRSTUVWXYZ".characters)
let lowercase = Array("abcdefghijklmnopqrstuvwxyz".characters)

(备注: 你(几乎)永远不想调用 xxxLiteral: 初始化器 明确地。这样做(几乎)总是隐藏实际问题。)

现在您填充字典的代码可以工作了:

for i in 0 ..< 26 {
    key[uppercase[i]] = uppercase[(i + 13) % 26]
    key[lowercase[i]] = lowercase[(i + 13) % 26]
}

并且可以将字符串转换为

//                       map
// String --> Characters ---> Characters -> String

func rot13(s: String) -> String {
    return String(s.characters.map { key[$0] ?? $0 })
}

【讨论】:

    【解决方案2】:

    这是另一种方法,它不使用查找数组:

    let input = "Hello World"
    
    func rot13(unicodeScalar: UnicodeScalar) -> UnicodeScalar {
        var result = unicodeScalar.value
    
        if 65...90 ~= result { //Detect capital A ... Z
            result = (result + 13 - 65) % 26 + 65
        }
        else if 97 ... 122 ~= result { //Detect lowercase a ... z
            result = (result + 13 - 97) % 26 + 97
        }
    
        return UnicodeScalar(result)
    }
    
    func rot13(_ input: String) -> String {
        let resultUSs = input.unicodeScalars.map(rot13)
    
        var resultUSV = String.UnicodeScalarView()
        resultUSV.appendContentsOf(resultUSs) //for Swift 2.2
        //resultUSV.append(contentsOf: resultUSs) //for Swift 3.0
        return String(resultUSV)
    }
    
    let output = rot13(input)
    
    print(output)
    

    【讨论】:

    • 你的 Swift 3 正在展示。您可能需要注意 resultUSV.appendContentsOf(resultUSs) 以了解 Swift 2.2 的兼容性。
    • 啊,是的,我是在 IBM 的 Swift 沙箱中编写的,它只有 Swift 3
    • 是的,我希望他们能让你选择 Swift 2.2 和 Swift 3。这将是一段时间的过渡时间,直到 Xcode 8 成为 GM。
    • 最后一件事:return UnicodeScalar(result) 需要一个大写字母 U
    • 谢谢 ;) 现在修复它
    【解决方案3】:

    这是@AMomchilov 的rot13 的替代版本,它使用switch 和更少的数学并消除了幻数:

    func rot13(_ unicodeScalar: UnicodeScalar) -> Character {
        var result = unicodeScalar.value
        
        switch unicodeScalar {
        case "A"..."M", "a"..."m":
            result += 13
        case "N"..."Z", "n"..."z":
            result -= 13
        default:
            break
        }
        
        return Character(UnicodeScalar(result)!)
    }
    
    func rot13(_ input: String) -> String {
        return String(input.unicodeScalars.map(rot13))
    }
    
    print(rot13("Uryyb, jbeyq!")) // "Hello, world!"
    

    泛化到 rotN

    我采用了上面的 rot13 函数,并通过让它们采用 ClosedRange&lt;UnicodeScalar&gt; 的数组将它们概括为 rotN。这允许您以非常直接的方式实现rot13rot47rot5 以及rot13rot5 的组合。

    func rotN(_ unicodeScalar: UnicodeScalar, intervals:[ClosedRange<UnicodeScalar>]) -> Character {
        var result = unicodeScalar.value
    
        for interval in intervals {
            let half = (interval.upperBound.value - interval.lowerBound.value + 1) / 2
            let halfway = UnicodeScalar(interval.lowerBound.value + half)!
    
            switch unicodeScalar {
            case interval.lowerBound..<halfway:
                result += half
            case halfway...interval.upperBound:
                result -= half
            default:
                break
            }
        }
    
        return Character(UnicodeScalar(result)!)
    }
    
    func rotN(_ input: String, intervals:[ClosedRange<UnicodeScalar>]) -> String {
        return String(input.unicodeScalars.map {rotN($0, intervals: intervals)})
    }
    
    func rot13(_ input: String) -> String {
        return rotN(input, intervals:["A"..."Z", "a"..."z"])
    }
    
    func rot47(_ input: String) -> String {
        return rotN(input, intervals:["!"..."~"])
    }
    
    func rot5(_ input: String) -> String {
        return rotN(input, intervals:["0"..."9"])
    }
    
    func rot13and5(_ input: String) -> String {
        return rotN(input, intervals:["A"..."Z", "a"..."z", "0"..."9"])
    }
    
    print(rot13("Uryyb, jbeyq!"))        // "Hello, world!"
    print(rot47("%96 BF:4< 3C@H? 7@I"))  // "The quick brown fox"
    print(rot5("6 + 7 = 8"))             // "1 + 2 = 3"
    print(rot13and5("Whyl 9, 6221"))     // "July 4, 1776"
    

    这是基于@AMomchilov's原始rot13rotN版本:

    func rotN(_ unicodeScalar: UnicodeScalar, intervals:[ClosedRange<UnicodeScalar>]) -> UnicodeScalar {
        var result = unicodeScalar.value
        
        for interval in intervals {
            let start = interval.lowerBound.value
            let length = interval.upperBound.value - start + 1
            
            if interval ~= unicodeScalar {
                result = (result + length/2 - start) % length + start
            }
        }
        
        return UnicodeScalar(result)!
    }
    
    func rotN(_ input: String, intervals:[ClosedRange<UnicodeScalar>]) -> String {
        return String(input.unicodeScalars.map {Character(rotN($0, intervals:intervals))})
    }
    

    【讨论】:

    • 哦,这是一个有趣的方法。我喜欢用于范围检查的开关。我忘记了这是可能的;它太有用了!不过,我不喜欢删除 mod 13 的解决方法,因为它很难将其推广到 rotN。我认为更好的解决方案是将我的 2 条数学线分解为一个单独的参数化函数。
    • @AMomchilov,我接受了你的挑战并推广到rotN :-)。
    • 不错,但我仍然追求避免% 增加了不必要的复杂性
    • @AMomchilov,我根据您的原始代码添加了一个基于% 的版本。我已将其更改为返回 Character 以使调用代码更简单。
    • 我喜欢!我们走了!
    猜你喜欢
    • 2021-11-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-11
    • 2015-11-30
    • 2014-09-07
    相关资源
    最近更新 更多