2015-05-30 102 views
1

目标是让人输入一个整数,并验证它确实是一个整数。如果不是,则再询问一次。 第一次尝试是使用ioMonad.whileM, becasue它实际上里面IO返回值,并写类似的东西(然后我们就可以“安全地”投字符串INT):IORef是否正确使用?

val input: IO[Option[String]] = ioMonad.whileM(readLn.map(_ exists notDigit), 
askAndReadNumber)(scalaz.std.AllInstances.optionInstance) 

但这种方法做不工作,因为在这种情况下,我不仅验证了价值,而且还从控制台再次读取它。所以,因为我需要读取输入,然后以某种方式将它传递给条件,那么我认为,那可能是完全正确的工具(我以前从未使用过,因此请认为这是我的卑微尝试学习函数式编程)。 我结束了与:

def requireNumber: IO[Int] = {  
    val numref: IO[IORef[String]] = newIORef("a") 
    ioMonad.whileM_(condition(numref), askAndReadNumberToIORef(numref)) 
    numref.flatMap(_.read).map(_.toInt) 
    } 

    def condition(num: IO[IORef[String]]): IO[Boolean] = for { 
    ref ← num 
    enteredNumber ← ref.read 
    } yield enteredNumber exists notDigit 

    def askAndReadNumberToIORef(num: IO[IORef[String]]): IO[Unit] = for { 
    ref ← num 
    input ← askAndReadNumber 
    _ ← ref.write(input) 
    } yield() 

    private def notDigit: (Char) ⇒ Boolean = 
    !Character.isDigit(_) 

    def askAndReadNumber: IO[String] = 
    for { 
    _ ← putStrLn("Enter a number please") 
    maxN ← readLn 
    } yield maxN 

什么发生的事情是 - 实际上是整个循环被完全忽略,程序直接到初始参考线:

num.flatMap(_.read).map(_.toInt) 

这样,我误用参考这里的概念?为什么它不起作用?

感谢

更新: 其实我写这个方法解决了最初的问题:

def whileMpropagating[M[_], A](f: ⇒ M[A])(p: A ⇒ Boolean)(implicit M: Monad[M]): M[A] = 
    M.bind(f)(value ⇒ if (!p(value)) f else whileMpropagating(f)(p)) 

然后whileMpropagating(askAndReadNumber)(_ forall notDigit)(ioMonad) map (_.toInt)

但我仍然有兴趣在这里利用IOREF。

UPDATE2:我的耻辱,iterateWhile在单子做exaclty这样的:)

回答

1

IORef是矫枉过正的情况。这是在几行代码的溶液:

import scala.util.Try 
import scalaz.effect.IO 
import scalaz.syntax.monad._ 

private def isInteger(str: String) = Try(Integer.parseInt(str)).isSuccess 

val requireNumber: IO[Int] = IO.readLn.iterateUntil(isInteger).map(Integer.parseInt) 

IORef表示一个可变的参考(其功能编程试图避免),并且应当非常谨慎使用。首先尝试编写纯函数来解决您的问题总是一个好主意。