2011-11-16 4 views
9

List [Option [Int]]가 있고 적용 할 수있는 Functor를 사용하여 합계하고 싶습니다. 에서 [1] 나는 그러나 단순히이 쓸 수있는 올바른 방법을 알아낼 수 아니다는 다음적용 할 Functor로 옵션 목록 합계

import scalaz._ 
import Scalaz._ 

List(1,2,3).map(some(_)).foldLeft(some(0))({ 
    case (acc,value) => (acc <|*|> value){_+_} 
}) 

처럼 뭔가를해야한다고 알고 있습니다. 누군가 내가 이것으로 나를 도울 수 있으면 기뻐할 것입니다.

는 당신에게 모든 위대한 답변을

[1] How to combine Option values in Scala?

편집

감사합니다 대단히 감사합니다.

목록에 없음이 있으면 None을 반환합니다. Null/Exception을 Option/Either로 바꾸고 사용 가능한 코드를 생성 할 수 있는지 확인하려고합니다.

일부 기능은 내 목록을 채울 것이고 요소 중 하나가 없음인지 확인하지 않고 가능한 한 쉽게 처리하려고합니다. Exceptions와 유사하게 작동해야하는데, 여기서 함수에서 점검 할 필요는 없지만 호출자가 처리하도록하십시오.

+0

안녕하세요 마누엘! 예. 매우 중요한 부분은 항상 처리 방법입니다. 없음 : 무시하거나 실패하면 관련 예제를 참조하십시오. https://gist.github.com/970717 – AndreasScheinert

+0

안녕하세요 Andreas. 너 같은 코드 조각이 내가 필요한거야. –

답변

9

당신이 Option[T]을 가지고 T에 대한 Monoid이 있다면, 다음있을 경우 Monoid[Option[T]] : 당신이 장착되면

implicit def optionTIsMonoid[T : Monoid]: Monoid[Option[T]] = new Monoid[Option[T]] { 
    val monoid = implicitly[Monoid[T]] 
    val zero = None 
    def append(o1: Option[T], o2: =>Option[T]) = (o1, o2) match { 
    case (Some(a), Some(b)) => Some(monoid.append(a, b)) 
    case (Some(a), _)  => o1 
    case (_, Some(b))  => o2 
    case _     => zero 
    } 
} 

, 당신은 할 수 sum (@missingfaktor에서 제안한대로 foldMap(identity) 이상)을 사용하십시오.

List(Some(1), None, Some(2), Some(3), None).asMA.sum === Some(6) 

UPDATE

우리는 실제로 위의 코드를 단순화하기 위해 applicatives를 사용할 수 있습니다

implicit def optionTIsMonoid[T : Monoid]: Monoid[Option[T]] = new Monoid[Option[T]] { 
    val monoid = implicitly[Monoid[T]] 
    val zero = None 
    def append(o1: Option[T], o2: =>Option[T]) = (o1 |@| o2)(monoid.append(_, _)) 
} 

날 우리는 어쩌면에 더 일반화 수 있다고 생각한다 : 그런

implicit def applicativeOfMonoidIsMonoid[F[_] : Applicative, T : Monoid]: Monoid[F[T]] = 
    new Monoid[F[T]] { 
    val applic = implicitly[Applicative[F]] 
    val monoid = implicitly[Monoid[T]] 

    val zero = applic.point(monoid.zero) 
    def append(o1: F[T], o2: =>F[T]) = (o1 |@| o2)(monoid.append(_, _)) 
    } 

을 목록의 목록, 나무 목록을 합칠 수 있습니다.

UPDATE2

문제의 설명은 나를 UPDATE 위 잘못되었음을 깨닫게!입력리스트에서 None을 거기로 두번째 빨리 None를 리턴하면서 제 정의 None 값을 이동하기 때문에 리팩토링 모든 optionTIsMonoid

먼저, 첫 번째 정의 동등하지 않다. 하지만이 경우에는 Monoid이 아닙니다! 실제로 Monoid[T]은 Monoid 법칙을 준수해야하며 zeroidentity 요소 여야합니다.

zero |+| Some(a) = Some(a) 
Some(a) |+| zero = Some(a) 

을하지만 OptionApplicative를 사용하여 Monoid[Option[T]]에 대한 정의를 제안 할 때, 이러한 경우가 있었다 :

우리가해야

None |+| Some(a) = None 
None |+| None = None 
=> zero |+| a  != a 

Some(a) |+| None = zero 
None |+| None = zero 
=> a |+| zero != a 

수정하는 것은 우리는 어렵지 않다 zero의 정의를 변경해야합니다.

// the definition is renamed for clarity 
implicit def optionTIsFailFastMonoid[T : Monoid]: Monoid[Option[T]] = 
    new Monoid[Option[T]] { 
    monoid = implicitly[Monoid[T]] 
    val zero = Some(monoid.zero) 
    append(o1: Option[T], o2: =>Option[T]) = (o1 |@| o2)(monoid.append(_, _)) 
    } 
신원 법이 확인 증명

Some(0) |+| Some(i) = Some(i) 
Some(0) |+| None = None 
=> zero |+| a  = a 

Some(i) |+| Some(0) = Some(i) 
None |+| Some(0) = None 
=> a |+| zero = zero 

은 (우리는 또한 ...을 associative 법이 존중되고 있는지 확인해야합니다) :이 경우 691,363,210

우리는 (IntT로)해야합니다.

이제 우리는리스트를 요약 할 때 원하는대로 동작 할 수있는 Monoid[Option[T]]을 가지고 있습니다 : None을 건너 뛰거나 "빠름".

+0

이 글을 게시 한 직후, 나는 어떤 Applicative도 사용하고 있지 않기 때문에 나는 그 질문에 정말로 대답하지 않는다는 것을 알았다. 이것을 단지 수많은 대안 중 하나로 간주하십시오. – Eric

+0

어떻게 작동하는지 이해할 수 없습니다 ... 왜 아무 곳에도 + +가 없습니까? – Owen

+1

'.foldMap (identity)'는'.asMA.sum'으로 대체 될 수 있습니다. – missingfaktor

5

하나의 옵션은 다음 정규처럼 배, 먼저 전체 목록을 시퀀싱하는 것입니다 :

val a: List[Option[Int]] = List(1, 2, 3) map (Some(_)) 
a.sequence map (_.foldLeft(0)(_+_)) 
+2

아니면, 그냥. 시퀀스 맵 {_.sum} – Submonoid

14

당신은 정말이에 대한 Scalaz 필요하지 않습니다. 목록을 평평하게 만들면 인 항목을 모두 제거하여 List[Int]으로 변환합니다. 그런 다음 당신이 그것을 줄일 수

List(Some(1), None, Some(2), Some(3), None).flatten.reduce(_ + _) //returns 6: Int 
+0

"None"이라면 결과를 'None'으로 만들고 싶다고 해석했지만, 이제는 내가 언급했음을 확신 할 수 없다. 맞아 ... – Owen

+0

아주 좋은 점은 적어도 하나의 항목이 None이거나 Nones를 무시하고 값이있는 항목을 합한 경우 합계가 실패 할지를 OP가 지정해야한다고 가정합니다. –

+6

또는'.flatten.sum'. – missingfaktor

6
scala> List(1, 2, 3).map(some).foldLeft(0 some) { 
    | case (r, c) => (r |@| c)(_ + _) 
    | } 
res180: Option[Int] = Some(6) 
0

Scalaz의 ApplicativeBuilder는 다른 옵션입니다. 이 곳 얼마 전에 찾을 수

import scalaz._ 
import Scalaz._ 

List(1,2,3).map(_.some).foldl1((acc,v) => (acc |@| v) {_+_}) join 
0

, 더 이상 소스를 찾을 수 있지만, 그것은 나를 위해 일하고있다

def sumOpt1(lst: List[Option[Int]]): Option[Int] = { 
    lst.foldLeft(Option.empty[Int]) { 
     case (prev, elem) => 
     (prev, elem) match { 
      case (None, None) => None 
      case (None, Some(el)) => Some(el) 
      case (Some(p), None) => Some(p) 
      case (Some(p), Some(el)) => Some(p + el) 
     } 
    } 
    } 

또는

def sumOpt2(lst: List[Option[Int]]): Option[Int] = { 
    lst.foldLeft(Option.empty[Int]) { 
     case (prev, elem) => 
     (prev, elem) match { 
      case (None, None) => None 
      case (p, el) => Some(p.getOrElse(0) + el.getOrElse(0)) 
     } 
    } 
    } 

또는

def sumOpt3(lst: List[Option[Int]]): Option[Int] = { 
    lst.foldLeft(Option.empty[Int]) { 
     case (prev, elem) => 
     (prev, elem) match { 
      case (None, el) => el 
      case (p, None) => p 
      case (Some(p), Some(el)) => Some(p + el) 
     } 
    } 
    }