【问题标题】:Functional thinking in Swift?Swift 中的函数式思维?
【发布时间】:2015-01-06 22:18:39
【问题描述】:

我开始学习 Haskell 和 Swift。我想知道这是否是以“功能”方式思考的正确方式? 问题是创建卡片组: 我需要遍历套装和等级 - 为每个套装创建具有给定套装和等级的卡片。以“势在必行”的方式:

let suits: Array<Character> = ...
let ranks: Array<Int> = ...

var cards: [Card]
for suit in suits {
    for rank in ranks {
        cards.addObject(Card(suit: suit, rank: rank))
    }
}

然后我尝试使用递归的纯函数,它可以工作,但是可以用更少的代码来完成吗? 对我来说,Swift 中的“函数式”可读性较差,或者我可能做错了......

let cards = cardsWithSuits(suits, ranks, [Card]());

func cardsWithSuits(suits: [Character], ranks: [Int], cards: [Card]) -> [Card] {
   if suits.count == 0 { return cards }
   let suit: Character = head(suits)!
   let acc = cardsWithRanks(ranks, suit, cards)

   return cardsWithSuits(drop(1, suits), ranks, acc)
}

func cardsWithRanks(ranks: [Int], suit: Character, cards: [Card]) -> [Card] {
   if ranks.count == 0 { return cards }
   let acc = cards + [Card(suit: suit, rank: head(ranks)!)]

   return cardsWithRanks(drop(1, ranks), suit, acc)
}

【问题讨论】:

  • 在 Haskell 中,您可能会使用列表解析:[Card s r | s &lt;- suits, r &lt;- ranks]。你也可以利用 list 是一个 Applicative 的事实并写 Card &lt;$&gt; suits &lt;*&gt; ranks 虽然工作的原因有点远不明显。
  • 以上转换为concat (concat (map (\suit -&gt; map (\rank -&gt; Card rank suit) ranks) suits)),顺便说一句,这可能在Swift中更直接可以实现。
  • Haskell 更加简洁,不幸的是在 Swift 中没有类似的语法。我在看了 Haskell 教程之后在上面写了,然后我实现了一些简单的 Haskell 函数。在 swift (head, drop ...) 中尝试我是否可以在 swift 中做到这一点,但在 swift 中它更加冗长。
  • 哪个是其他人最易读/最容易理解的?这是决定好代码的指标。

标签: haskell swift functional-programming


【解决方案1】:

在使用 Haskell 的应用概念以及 &lt;$&gt;&lt;*&gt; 的基础上,您可能会发现以下内容通常很有用(我想我已经正确翻译了,尽管它基于数组而不是序列):

// use <^> because <$> is already used
infix operator <^> { associativity left }
public func <^> <T, U>(left:(T)->U, right:[T]) -> [U] {
    return map(right) { return left($0) }
}

public func flatten<T>(input:[[T]]) -> [T] {
    return input.reduce([], +)
}

infix operator <*> { associativity left }
public func <*> <T, U>(left:[(T)->U], right:[T]) -> [U] {
    return flatten(map(left) { (function) -> [U] in
        return map(right) { return function($0) }
    })
}

然后允许您使用以下内容:

let suits : [Character] = [ "C", "D", "H", "S"]
let ranks = Array(2...14)

struct Card {
    let suit : Character
    let rank : Int

    static func build(suit:Character)(rank:Int) -> Card {
        return Card(suit: suit, rank:rank)
    }
}

Card.build <^> suits <*> ranks

【讨论】:

  • 谢谢。它工作正常!我还没有听说过“Haskell 的应用概念”,我刚刚开始“Learn You a Haskell, for Great Good”。现在会检查它。
【解决方案2】:

这可能不漂亮,我不认为它是函数式编程,但它的代码更少,并且使用了 Swift 非常棒的 map 和 reduce 函数:

struct Card {
    let suit: String
    let rank: Int
}

let cards = ["Heart", "Diamond", "Club", "Spade"].reduce([Card]()) { (cards, suit) in
    return cards + map(1...13) { rank in return Card(suit: suit, rank: rank) }
}

【讨论】:

  • 不幸的是,这并没有回答 OP 提出的问题
  • 它应该是有效的纯函数式“风格”。完美结合reduce、map来实现目标。
  • @ThreeFx:他确实问过是否可以用更少的代码完成任务,以及其他问题:) 但我同意这可能不是他想要的。我只是把它扔在那里进行比较。
【解决方案3】:

Swift 不是函数式语言。但是,您可以编写函数式的 swift 代码。

func map<T: Collection, U>( _ transform: (T.Iterator.Element) -> U, _ xs: T) -> [U] {
    return xs.reduce([U](), {$0 + [transform($1)]})
}

func concatMap<A, B> (_ process: (A)->[B], _ xs: [A]) -> [B] {
    return xs.reduce([B](), {$0 + process($1)})
}

infix operator <*>
func <*><A, B>(_ xs: [A], _ ys: [B]) -> [(A, B)]{
    let transform: (A, B) -> (A, B) = {($0, $1)}
    return concatMap({x in map({transform(x, $0)}, ys)}, xs)
}

struct Card {
    let suit : Character
    let rank : Int

    static func build(_ sr: (s:Character, r:Int)) -> Card {
        return Card(suit: sr.0, rank: sr.1)
    }
}

func test() {
    let suits : [Character] = [ "C", "D", "H", "S"]
    let ranks = Array(1...13)
    let cards = map(Card.build, suits <*> ranks)
    print(cards)
}

如果你想学习 Haskell 和 Swift,可以参考https://github.com/unchartedworks/HaskellSwift

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-03-15
    • 2010-12-21
    • 1970-01-01
    • 2019-01-12
    • 1970-01-01
    • 2015-09-03
    • 2017-10-04
    • 2015-03-23
    相关资源
    最近更新 更多