Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/react-native/7.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Functional programming 函数式编程中的实现状态_Functional Programming - Fatal编程技术网

Functional programming 函数式编程中的实现状态

Functional programming 函数式编程中的实现状态,functional-programming,Functional Programming,我目前正在使用Scala编写一个Android音乐播放器应用程序。我选择Scala是因为它的函数式编程功能,我希望使代码尽可能符合FP 由于FP意味着不变性,代码不应该携带任何状态,变量应该是不可变的。但我面临着一些复杂的用例,我不知道如何用纯函数编程的方式来解决 第一个是播放列表案例。音乐播放器正在播放列表中的一首歌曲。这可以用歌曲列表和指示当前播放歌曲的光标来表示。但当这首歌结束时,播放器必须播放下一首歌,因此,更改光标的值 播放列表本身也存在同样的问题:用户必须能够更改(添加或抑制歌曲)播

我目前正在使用Scala编写一个Android音乐播放器应用程序。我选择Scala是因为它的函数式编程功能,我希望使代码尽可能符合FP

由于FP意味着不变性,代码不应该携带任何状态,变量应该是不可变的。但我面临着一些复杂的用例,我不知道如何用纯函数编程的方式来解决

第一个是播放列表案例。音乐播放器正在播放列表中的一首歌曲。这可以用歌曲列表和指示当前播放歌曲的光标来表示。但当这首歌结束时,播放器必须播放下一首歌,因此,更改光标的值

播放列表本身也存在同样的问题:用户必须能够更改(添加或抑制歌曲)播放列表。如果播放列表本身是不可变的,则每当用户添加或抑制歌曲时,都会生成一个新的播放列表。但该播放列表必须受一个变量的影响,该变量必须是可变的

在这个应用程序中,我看到的每一处状态——播放器是否暂停?什么是当前歌曲,当前播放列表?设置的当前状态是什么?等等——我不知道如何用纯函数编程的方式来解决这个问题,也就是说,使用不可变变量


由于这些用例看起来相当标准,我想有一些设计模式可以解决它们(比如monad),但我不知道该去哪里寻找。

我写了一些库试图解决这个问题,结果相当糟糕

基本上,将活动、片段等转换为接受状态和返回状态的纯函数

这与IO monads结合在一起,使得界面有点纯净。下面是一个例子(PureActivity的源可以在中找到),在这种情况下的“状态”是“Option[Process]”,当logcat运行时进程存在,当logcat不运行时进程为空。无变量:

class LogcatActivity extends AppCompatActivity with PureActivity[Option[Process]] {
  val LOG_LINE = """^([A-Z])/(.+?)\( *(\d+)\): (.*?)$""".r
  val buffersize = 1024
  lazy val toolbar = newToolbar
  lazy val recycler = {
    val r = new RecyclerView(this)
    r.setLayoutManager(new LinearLayoutManager(this))
    r.setAdapter(Adapter)
    r
  }
  lazy val layout = l[LinearLayout](
    toolbar.!  >>= lp(MATCH_PARENT, WRAP_CONTENT),
    recycler.! >>= lp(MATCH_PARENT, 0, 1)
  ) >>= vertical

  override def initialState(b: Option[Bundle]) = None

  override def applyState[T](s: ActivityState[T]) = s match {
    case OnPreCreate(_) => s(IO(
      setTheme(if (Settings.get(Settings.DAYNIGHT_MODE)) R.style.SetupTheme_Light else R.style.SetupTheme_Dark)
    ))
    case OnCreate(_) => s(IO {
      toolbar.setTitle("Logcat")
      toolbar.setNavigationIcon(resolveAttr(R.attr.qicrCloseIcon, _.resourceId))
      toolbar.navigationOnClick0(finish())
      setContentView(layout.perform())
    })
    case OnStart(_) => s.applyState(IO {
      var buffering = true
      val logcat = "logcat" :: "-v" :: "brief" :: Nil
      val lineLogger = new ProcessLogger {
        override def out(s: => String) = addLine(s)
        override def buffer[X](f: => X) = f
        override def err(s: => String) = addLine(s)

        def addLine(line: String) = line match {
          case LOG_LINE(level, tag, pid, msg) =>
            if (tag != "ResourceType") UiBus.run {
              val c = Adapter.getItemCount // store in case at max items already
              Adapter.buffer += LogEntry(tag, level, msg)
              Adapter.notifyItemInserted(math.min(buffersize, c + 1))
              if (!buffering)
                recycler.smoothScrollToPosition(Adapter.getItemCount)
            }
          case _ =>
        }
      }
      Future {
        Thread.sleep(500)
        buffering = false
      } onSuccessMain { case _ =>
        recycler.scrollToPosition(Adapter.getItemCount - 1)
      }
      logcat.run(lineLogger).?
    })
    case OnStop(proc) => s.applyState(IO {
      proc.foreach(_.destroy())
      None
    })
    case x => defaultApplyState(x)
  }

  case class LogEntry(tag: String, level: String, msg: String)
  case class LogcatHolder(view: TextView) extends RecyclerView.ViewHolder(view) {
    def bind(e: LogEntry): Unit = view.setText(" %1 %2: %3" formatSpans (
      textColor(MessageAdapter.nickColor(e.level), e.level),
      textColor(MessageAdapter.nickColor(e.tag),   e.tag), e.msg))
  }
  object Adapter extends RecyclerView.Adapter[LogcatHolder] {
    val buffer = RingBuffer[LogEntry](buffersize)
    override def getItemCount = buffer.size
    override def onBindViewHolder(vh: LogcatHolder, i: Int) = vh.bind(buffer(i))

    override def onCreateViewHolder(viewGroup: ViewGroup, i: Int) = {
      val tv = new TextView(LogcatActivity.this)
      tv.setTypeface(Typeface.MONOSPACE)
      LogcatHolder(tv)
    }
  }
}

我写了一些库试图解决这个问题,结果相当糟糕

基本上,将活动、片段等转换为接受状态和返回状态的纯函数

这与IO monads结合在一起,使得界面有点纯净。下面是一个例子(PureActivity的源可以在中找到),在这种情况下的“状态”是“Option[Process]”,当logcat运行时进程存在,当logcat不运行时进程为空。无变量:

class LogcatActivity extends AppCompatActivity with PureActivity[Option[Process]] {
  val LOG_LINE = """^([A-Z])/(.+?)\( *(\d+)\): (.*?)$""".r
  val buffersize = 1024
  lazy val toolbar = newToolbar
  lazy val recycler = {
    val r = new RecyclerView(this)
    r.setLayoutManager(new LinearLayoutManager(this))
    r.setAdapter(Adapter)
    r
  }
  lazy val layout = l[LinearLayout](
    toolbar.!  >>= lp(MATCH_PARENT, WRAP_CONTENT),
    recycler.! >>= lp(MATCH_PARENT, 0, 1)
  ) >>= vertical

  override def initialState(b: Option[Bundle]) = None

  override def applyState[T](s: ActivityState[T]) = s match {
    case OnPreCreate(_) => s(IO(
      setTheme(if (Settings.get(Settings.DAYNIGHT_MODE)) R.style.SetupTheme_Light else R.style.SetupTheme_Dark)
    ))
    case OnCreate(_) => s(IO {
      toolbar.setTitle("Logcat")
      toolbar.setNavigationIcon(resolveAttr(R.attr.qicrCloseIcon, _.resourceId))
      toolbar.navigationOnClick0(finish())
      setContentView(layout.perform())
    })
    case OnStart(_) => s.applyState(IO {
      var buffering = true
      val logcat = "logcat" :: "-v" :: "brief" :: Nil
      val lineLogger = new ProcessLogger {
        override def out(s: => String) = addLine(s)
        override def buffer[X](f: => X) = f
        override def err(s: => String) = addLine(s)

        def addLine(line: String) = line match {
          case LOG_LINE(level, tag, pid, msg) =>
            if (tag != "ResourceType") UiBus.run {
              val c = Adapter.getItemCount // store in case at max items already
              Adapter.buffer += LogEntry(tag, level, msg)
              Adapter.notifyItemInserted(math.min(buffersize, c + 1))
              if (!buffering)
                recycler.smoothScrollToPosition(Adapter.getItemCount)
            }
          case _ =>
        }
      }
      Future {
        Thread.sleep(500)
        buffering = false
      } onSuccessMain { case _ =>
        recycler.scrollToPosition(Adapter.getItemCount - 1)
      }
      logcat.run(lineLogger).?
    })
    case OnStop(proc) => s.applyState(IO {
      proc.foreach(_.destroy())
      None
    })
    case x => defaultApplyState(x)
  }

  case class LogEntry(tag: String, level: String, msg: String)
  case class LogcatHolder(view: TextView) extends RecyclerView.ViewHolder(view) {
    def bind(e: LogEntry): Unit = view.setText(" %1 %2: %3" formatSpans (
      textColor(MessageAdapter.nickColor(e.level), e.level),
      textColor(MessageAdapter.nickColor(e.tag),   e.tag), e.msg))
  }
  object Adapter extends RecyclerView.Adapter[LogcatHolder] {
    val buffer = RingBuffer[LogEntry](buffersize)
    override def getItemCount = buffer.size
    override def onBindViewHolder(vh: LogcatHolder, i: Int) = vh.bind(buffer(i))

    override def onCreateViewHolder(viewGroup: ViewGroup, i: Int) = {
      val tv = new TextView(LogcatActivity.this)
      tv.setTypeface(Typeface.MONOSPACE)
      LogcatHolder(tv)
    }
  }
}

你说的是用户界面。它本质上是有状态的。如果没有国家,你不能也不能使用它。只有一种正确的方法:将没有状态的代码与有状态的代码分开

最好的概念是FRP-。它将功能部件和具有可变状态内容的不可变框分开,并通过事件将它们连接起来

请注意,网络上许多所谓的反应式编程技术实际上并不是这样的,只是宣称是反应式的。例如,java RX是绝对无效的,并且缺少两个非常重要的特性。(隐藏侦听器和同时性支持)


关于这个问题有一个很好的讨论。它也可以在网上的一些动作中找到。作者给出了开源基础库和swift FRP支持库,它们可以作为一种模式,用于根据您的需要创建自己的FRP类

你说的是用户界面。它本质上是有状态的。如果没有国家,你不能也不能使用它。只有一种正确的方法:将没有状态的代码与有状态的代码分开

最好的概念是FRP-。它将功能部件和具有可变状态内容的不可变框分开,并通过事件将它们连接起来

请注意,网络上许多所谓的反应式编程技术实际上并不是这样的,只是宣称是反应式的。例如,java RX是绝对无效的,并且缺少两个非常重要的特性。(隐藏侦听器和同时性支持)


关于这个问题有一个很好的讨论。它也可以在网上的一些动作中找到。作者给出了开源基础库和swift FRP支持库,它们可以作为一种模式,用于根据您的需要创建自己的FRP类

“FP兼容”-没有“FP兼容”这样的东西-没有这样的东西这在intellij中也很糟糕,因为答案非常复杂。谢谢!这在intellij中也很突出,因为答案非常复杂。谢谢!