2015-05-19 20 views
5

Tôi đang viết một hàm nhận một số giá trị String tùy chọn và chuyển đổi từng giá trị thành Int hoặc Boolean rồi chuyển giá trị được chuyển đổi sang Unit để xử lý tiếp. Nếu bất kỳ chuyển đổi nào bị lỗi, toàn bộ chức năng sẽ không thành công với lỗi. Nếu tất cả chuyển đổi thành công, hàm sẽ xử lý các giá trị được chuyển đổi và trả về thành công.Chuyển đổi nhiều giá trị tùy chọn trong Scala

Dưới đây là chức năng tôi đã viết (giản thể từ thực tế):

f(x: Option[String], y: Option[String], z: Option[String]): Result = { 
    val convertX = x.map(value => Try(value.toInt)) 
    val convertY = y.map(value => Try(value.toBoolean)) 
    val convertZ = z.map(value => Try(value.toBoolean)) 

    val failuresExist = 
    List(convertX, convertY, convertZ).flatten.exists(_.isFailure) 

    if (failuresExist) BadRequest("Cannot convert input") 
    else { 
    convertX.foreach { 
     case Success(value) => processX(value) 
     case _ => 
    } 

    convertY.foreach { 
     case Success(value) => processY(value) 
     case _ => 
    } 

    convertZ.foreach { 
     case Success(value) => processZ(value) 
     case _ => 
    } 

    Ok() 
    } 
} 

Mặc dù giải pháp này có thể sẽ làm việc, nó là rất khó xử. Làm thế nào tôi có thể cải thiện nó?

+1

đối với làm việc, mã có khả năng có thể làm tốt thêm tôi nghĩ rằng một nơi tốt đẹp hơn là http://codereview.stackexchange.com/ Trong trường hợp nó không hoạt động, bạn có thể giải thích vấn đề theo ý kiến ​​của bạn không? –

+0

@ GáborBakos Đây là một câu hỏi khá cụ thể về những gì có vẻ là một phương pháp mất tích. Tôi nghĩ ở đây ổn. –

+0

Tôi không rõ ràng, tôi đang viết một bộ điều khiển cho một ứng dụng Play. Có (hiện tại) 3 tham số truy vấn tùy chọn mà tôi cần xử lý. Tôi thà làm tất cả trong một cuộc gọi vì chúng có liên quan. – Ralph

Trả lời

0

Để hoàn thành, tôi đang thêm một đoạn mã vào đây để xử lý các giá trị được yêu cầu. Tuy nhiên nếu điều này là tốt hơn so với bản gốc là gây tranh cãi. Nếu bạn muốn xử lý tất cả giá trị và thu thập kết quả của phép biến đổi scalaz Validator có thể là một lựa chọn tốt hơn.

import scala.util.Try 

val x = Some("12") 
val y = Some("false") 
val z = Some("hello") 

def process(v: Boolean) = println(s"got a $v") 
def processx(v: Int) = println(s"got a number $v") 

// Abstract the conversion to the appropriate mapping 
def mapper[A, B](v: Option[String])(mapping: String => A)(func: Try[A] => B) = for { 
    cx <- v.map(vv => Try(mapping(vv))) 
    } yield func(cx) 

def f(x: Option[String], y: Option[String], z: Option[String]) = { 
    //partially apply the function here. We will use that method twice. 
    def cx[B] = mapper[Int, B](x)(_.toInt) _ 
    def cy[B] = mapper[Boolean, B](y)(_.toBoolean) _ 
    def cz[B] = mapper[Boolean, B](z)(_.toBoolean) _ 

    //if one of the values is a failure then return the BadRequest, 
    // else process each value and return ok 
    (for { 
    vx <- cx(_.isFailure) 
    vy <- cy(_.isFailure) 
    vz <- cz(_.isFailure) 
    if vx || vy || vz 
    } yield { 
    "BadRequest Cannot convert input" 
    }) getOrElse { 
    cx(_.map(processx)) 
    cy(_.map(process)) 
    cz(_.map(process)) 
    "OK" 
    } 

} 
f(x,y,z) 

Trong trường hợp hoạt động "ngắn mạch" là bắt buộc, mã sau sẽ hoạt động.

import scala.util.Try 

val x = Some("12") 
val y = Some("false") 
val z = Some("hello") 


def process(v: Boolean) = println(s"got a $v") 
def processx(v: Int) = println(s"got a number $v") 

def f(x: Option[String], y: Option[String], z: Option[String]) = 
    (for { 
    cx <- x.map(v => Try(v.toInt)) 
    cy <- y.map(v => Try(v.toBoolean)) 
    cz <- z.map(v => Try(v.toBoolean)) 
    } yield { 
    val lst = List(cx, cy, cz) 
     lst.exists(_.isFailure) match { 
     case true => "BadRequest Cannot convert input" 
     case _ => 
      cx.map(processx) 
      cy.map(process) 
      cz.map(process) 
      "OK" 
     } 
    }) getOrElse "Bad Request: missing values" 

f(x,y,z) 
+0

Tôi không tin rằng nó sẽ hoạt động, vì nó sẽ gắn cờ các giá trị bị thiếu (tùy chọn) dưới dạng lỗi. – Ralph

0

Kiểu dáng bắt buộc hơn có thể hoạt động, nếu bạn không bận tâm điều đó.

def f(x: Option[String], y: Option[String], z: Option[String]): Result = { 
    try { 
     val convertX = x.map(_.toInt) 
     val convertY = y.map(_.toBoolean) 
     val convertZ = z.map(_.toBoolean) 
     convertX.foreach(processX) 
     convertY.foreach(processY) 
     convertZ.foreach(processZ) 
     Ok() 
    } catch { 
     case _: IllegalArgumentException | _: NumberFormatException => BadRequest("Cannot convert input") 
    } 
} 
0

Nếu bạn đang sử dụng scalaz, tôi sẽ sử dụng bộ phối hợp Option applicative và ApplicativeBuilder |@|. Nếu bất kỳ đầu vào nào là None, thì kết quả cũng là None.

import scalaz.std.option.optionInstance 
import scalaz.syntax.apply._ 
val result: Option[String] = 
    Some(1) |@| Some("a") |@| Some(true) apply { 
    (int, str, bool) => 
     s"int is $int, str is $str, bool is $bool" 
    } 

Trong scala tinh khiết, bạn có thể sử dụng flatMap vào tùy chọn:

val result: Option[String] = 
    for { 
    a <- aOpt 
    b <- bOpt 
    c <- cOpt 
    } yield s"$a $b $c" 

Cá nhân tôi thích applicative bởi vì nó làm cho nó rõ ràng rằng kết quả là độc lập. cho khối đọc cho tôi như "đầu tiên làm điều này với một, sau đó điều này với b, sau đó điều này với c" trong khi phong cách applicative là giống như "với tất cả a, b, và c, làm ..."

một tùy chọn khác với scalaz là sequence, mà đảo ngược một cấu trúc giống như T[A[X]] vào A[T[X]] cho T traversable và applicative A.

import scalaz.std.option.optionInstance 
import scalaz.std.list.listInstance 
import scalaz.syntax.traverse._ 
val list: List[Option[Int]] = List(Option(1), Option(4), Option(5)) 
val result: Option[List[Int]] = list.sequence 
// Some(List(1, 4, 5)) 
Các vấn đề liên quan