LoginSignup
2

More than 5 years have passed since last update.

SwiftでMonoidにトライしてみた

Last updated at Posted at 2015-09-06

元の資料は市ヶ谷Geek★Night #3で発表されていた「ループで遊ぼう」です。

Swift2.0でやりました。


前提

ProductItemCode の構造体かクラスを作る。 Productname を持っていれば何でもいい

struct Product {
    let id: Int
    let name: String
}

struct Item {
}

struct Code {
}

Product から Itemを、 StringItem から Code の配列を作る関数を用意しておく。これも作れさえすればなんでもいい

func ItemMake(product: Product) -> Item {
    return Item()
}
func CodesMake(name: String, item: Item) -> [Code] {
    return [Code()]
}

そして今回のお題に使うもとになる Product を適当に配列で用意して準備完了

let products = [
    Product(id: 0, name: "ぜろ"),
    Product(id: 1, name: "いち"),
    Product(id: 2, name: "に")
]

本題

for文あり

きもい

var allItemsBuf = [Item]()
var allCodesBuf = [Code]()

for product in products {
    let item = ItemMake(product)
    allItemsBuf.append(item)
    allCodesBuf += CodesMake(product.name, item: item)
}

let allItems = allItemsBuf
let allCodes = allCodesBuf

map

このくらいだったら標準のやつですぐにやれて良い。

let allItems = products.map(ItemMake)
let allCodes = products.flatMap{ p in
    CodesMake(p.name, item: ItemMake(p))
}

Monoid

Monoidの準備

struct Monoid<T> {
    let op: (T, T) -> T
    let zero: T
}

let sum = Monoid<Int>(op: +, zero: 0)
func concatMake<T>(type: T.Type) -> Monoid<Array<T>> {
    return Monoid<Array<T>>(op: +, zero: [])
}

func foldMap<T,U>(array: [T], mb: Monoid<U>)(f: T -> U) -> U {
    return array.reduce(mb.zero){ a, b in
        mb.op(a, f(b))
    }
}

concat については sum と同様 let concat = ... みたいにやりたかったけど、そうすると Array<T>T を指定できなくなったので誤魔化した。もう少し調べたらやり方出てくるのかな?

元のScalaの資料では foldMapmb: Monoid<U>implicit で省略させる話が出てるけどSwiftでのやり方がよくわからないのでナシ。 protocol Monoid 作れば mb がなくても zeroop を呼び出せるけどそうすると後で出てくるtuple版の書き方がよくわからなくなる…。

Monoid(まだ無駄が残る版)

let allItems = foldMap(products, mb: concatMake(Item))(f: {p in [ItemMake(p)]})
let allCodes = foldMap(products, mb: concatMake(Code))(f: {p in CodesMake(p.name, item: ItemMake(p))})

Monoid(tuple使用版)

func tuple2<T,U>(ma: Monoid<T>, mb: Monoid<U>) -> Monoid<(T,U)> {
    return Monoid<(T,U)>(
        op: {(x: (T,U), y: (T,U)) -> (T,U) in
            (ma.op(x.0, y.0), mb.op(x.1, y.1))
        },
        zero: (ma.zero, mb.zero)
    )
}

let (allItems, allCodes) = foldMap(products, mb: tuple2(concatMake(Item), mb: concatMake(Code)))(f: {p in
            let item = ItemMake(p)
            return ([item], CodesMake(p.name, item: item))
        }
    )

Swiftだとtupleが0はじまりでfoldLeftがreduceでもうよくわからない


以上です。

mb を省略する手段と Monoid<T>Monoid<Array<T>> をどうにかしたかったです。難しいです。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2