scala - 在函数式编程怎么能够知道当前的时间?

  显示原文与译文双语对照的内容

我必须承认我对函数编程不太了解。 我从这里和那里了解到,在函数编程中,函数返回相同的输出,不管函数被调用多少次。 在函数中expression,这完全就是数学函数对于相同的输入参数值即它的计算为同一输出 involves.

例如考虑以下事项:


f(x,y) = x*x + y;//it is a mathematical function

无论你使用了多少次 f(10,4),它的值总是 104 。 因此,无论你在哪里编写 f(10,4),都可以用 104 替换它,而不改变整个表达式的值。 这里属性称为表达式的引用透明

维基百科上说( 链接 )

相反,在函数代码中,函数的输出值只依赖于函数的输入,因此调用参数为 f(x) 两次的函数将产生相同的结果两次。

所以我的问题是:时间函数( 这将返回的当前时间) 在函数编程中是否存在?

  • 如果是,那么它如何存在? 它是否违背了函数编程的原则? 它特别违反了引用透明,它是函数编程( 如果我正确理解它)的一个属性。

  • 或者如果不是,那么如何知道函数编程中的当前时间?

时间:

CP:是与不是.

不同的浮点语言解决它们。

在接触 Haskell ( 一个非常纯的) 这都是给情况发生时在用所谓的IO Monad - 看到这里 。 作为一个地方,你可以将它认为是得到另一种输入( world-state ) ( 和输出) 到你的函数或者容易"impureness"象买了不断变化的时间发生。

其他语言( 比如 F# 只是有些impureness植入和所以你可以有一个函数,该函数对于同样的输入会返回不同的值- 就像正常命令式语言。

就像 Jeffrey Burka在他的评论中提到的: 在这里的是美丽的有一幅画到 IO Monad正简介

在Haskell中,人们使用一个叫做 monad的构造来处理副作用。 一个单子基本上意味着你把值封装到一个容器中,并且有一些函数把函数从值到容器内的值。 如果我们的容器具有以下类型:


data IO a = IO (RealWorld -> (a,RealWorld))

我们可以安全地实现IO操作。 这里类型表示:类型 IO的操作是一个函数,它接受 RealWorld 类型的标记,并返回一个新的标记,以及一个结果。

这背后的思想是每个IO动作都会改变外部状态,由魔术标记 RealWorld 表示。 通过使用 monad,可以将多个函数链接到一起。 一个单子的最重要的功能是 >>=,发音是 。:


(>>=) :: IO a -> (a -> IO b) -> IO b

>>= 接受一个操作和一个函数,它接受这里操作的结果并从这个操作创建一个新动作。 返回类型是新动作。 比如说,假设有一个函数 now :: IO String ,它返回表示当前时间的字符串。 我们可以将它与函数 putStrLn 链接,以便打印出来:


now>> = putStrLn

或者用 do -Notation编写,这是一个命令式程序员更熟悉的:


do currTime <- now
 putStrLn currTime

所有这些都是纯粹的,因为我们将外界的变异和信息映射到 RealWorld 令牌。 所以每次你运行这个动作,你当然会得到一个不同的outut,但是输入是不一样的- RealWorld -token 。

另一种解释它的方法是: 没有( 因为它不断变化) 函数可以获取当前的时间,而一个 操作可以获取当前时间。 假设 getClockTime 是一个常量( 或者nullary函数,如果你喜欢),它代表当前时间的动作 。 无论使用什么时间,这个动作都是一样的,它是一个真实的常数。

同样,假设 print 是一个需要时间表示并将它的打印到控制台的函数。 由于函数调用在纯函数语言中不能有副作用,所以我们假设它是一个函数,它接受一个时间戳,并返回打印它到控制台的动作 。 同样,这是一个真正的函数,因为如果你给它同样的时间戳,它会在每次打印时返回相同的动作 。

现在,如何将当前时间打印到控制台? 你必须把这两个动作结合起来。 那我们该怎么做呢? 我们不能将 getClockTime 传递给 print,因为打印需要时间戳,而不是操作。 但我们可以想象有一个算,>>=,这结合两个操作,一个它获取一个时间戳以及一个该函数接受一个参数,也不要再打印出来。 将这里应用到前面提到的操作,结果是,。 tadaaa 。。一个获取当前时间并打印它的新动作。 这就是incidently如何在Haskell中完成。


Prelude> System.Time.getClockTime>> = print
Fri Sep 2 01:13:23 東京 (標準時) 2011

因此,在概念上,你可以通过这种方式查看它: 纯功能程序不执行任何 IO,它定义一个动作,运行时系统随后执行。 动作每次都是一样的,但是执行的结果取决于执行时的情况。

我不知道这是否比其他解释更清晰,但有时它可以帮助我这样思考。

大多数函数编程语言不是纯的,换句话说,允许函数不仅依赖于它们的值。 在这些语言中,有一个函数可以返回当前时间。 使用这里问题标记的语言适用于 Scala 和 F# ( 以及大多数其他的ML变种) 。

在Haskell和Clean之类的语言中,这种情况是不同的。 在Haskell中,当前的时间不能通过一个函数,而是一个所谓的IO动作,这就是封装副作用的Haskell 。

清洁它将是一个函数,但是函数将以一个世界值作为参数,并返回一个新的世界值( 除了当前时间) 作为它的结果。 类型系统将确保每个世界值只能使用一次( 每个消耗一个世界价值的函数都会产生一个新的) 。 这样,时间函数每次都需要用一个不同的参数调用,因此每次都允许返回不同的时间。

"当前时间"不是函数。 这是一个参数。如果你的代码依赖于当前时间,这意味着你的代码是按时间参数化的。

它绝对可以纯功能的方式完成。 有几种方法可以做到,但最简单的是让函数返回的时间而不仅仅是时间而且 该函数必须调用来获得下次测量。

在 C# 中,你可以像这样实现它:


//Exposes mutable time as immutable time (poorly, to illustrate by example)
//Although the insides are mutable, the exposed surface is immutable.
public class ClockStamp {
 public static readonly ClockStamp ProgramStartTime = new ClockStamp();
 public readonly DateTime Time;
 private ClockStamp _next;

 private ClockStamp() {
 this.Time = DateTime.Now;
 }
 public ClockStamp NextMeasurement() {
 if (this._next == null) this._next = new ClockStamp();
 return this._next;
 }
}

( 记住,这是一个简单的例子,而不是实际的) 。 特别是,List 节点不能被垃圾回收,因为它们是由ProgramStartTime根的。)

这个'ClockStamp'类像一个不可变的链接 List,但是实际上是按需求生成的,所以它们可以包含'当前'时间。 任何想要度量时间的函数都应该有一个'ClockStamp'参数,并且还必须在结果( 所以调用者看不到旧的测量) 中返回最后一次度量,如下所示:


//Immutable. A result accompanied by a clockstamp
public struct TimeStampedValue<T> {
 public readonly ClockStamp Time;
 public readonly T Value;
 public TimeStampedValue(ClockStamp time, T value) {
 this.Time = time;
 this.Value = value;
 }
}

//Times an empty loop.
public static TimeStampedValue<TimeSpan> TimeALoop(ClockStamp lastMeasurement) {
 var start = lastMeasurement.NextMeasurement();
 for (var i = 0; i <10000000; i++) {
 }
 var end = start.NextMeasurement();
 var duration = end.Time - start.Time;
 return new TimeStampedValue<TimeSpan>(end, duration);
}

public static void Main(String[] args) {
 var clock = ClockStamp.ProgramStartTime;
 var r = TimeALoop(clock);
 var duration = r.Value;//the result
 clock = r.Time;//must now use returned clock, to avoid seeing old measurements
}

当然,把最后的测量传入和流出,进出和流出都有点不便。 隐藏样板文件有很多方法,特别是在语言设计级别。 我认为Haskell使用这种技巧,然后用monads来隐藏丑陋的部分。

是你是正确的Now() 或者 CurrentTime() 或者这种味道的任何方法签名都没有以一种方式显示引用透明度。 但根据编译器的指令,它由系统时钟输入参数化。

按输出,Now() 可能看起来不符合引用透明度。 但是系统时钟的实际行为和上面的函数都遵循参照透明度。

是,获取时间函数可以在FP中使用,在FP上使用稍微修改过的( 默认的或者主要的是纯 FP ) 。

在获取时间的情况下,代码需要与外部世界交互以完成任务,而这个外部世界并不是基于( 或者读取文件,或者发射导弹)的纯粹基础。 为了让纯FP世界与外界不纯净的世界交互,人们引入了不纯净的FP 。 毕竟,除了做一些数学计算之外,没有与外界交互的软件是没有任何用处的。

很少有FP编程语言内置这种杂质特性,所以不容易分离出哪些代码是不纯净的,而哪些代码是纯( 像 F# 等) 和一些语言,如果你做的是与纯代码相对应的代码,比如 Haskell 。

另外我们能在看到这将是,如果你的FP中获取时间函数会将一"世界"对象,这里对象具有的当前状态和时间一样,许多人家居住在世界整个世界然后 等等 使得时间从那些世界对象可以总是纯 例如 你传递你只会得到同样的时间在同样的世界状态。

你的问题conflates关于计算机语言的两个相关度量: 功能/命令式和纯/不纯。

函数语言定义了函数输出和输出之间的关系,而命令式语言以特定的顺序描述特定的操作以执行特定的操作。

纯语言不创建或者依赖于副作用,而非纯语言则使用它们。

One-hundred百分比纯程序基本上毫无用处。 它们可能会执行一个有趣的计算,但是因为它们不能有副作用,它们没有输入或者输出,所以你不会知道它们是什么。

在所有的情况下,程序至少要稍微有点不对。 使纯程序有用的一种方法是将它放入一个不纯的包装中。 像这个未经测试的Haskell程序:


-- this is a pure function, written in functional style.
fib 0 = 0
fib 1 = 1
fib n = fib (n-1) + fib (n-2)

-- This is an impure wrapper around the pure function, written in imperative style
-- It depends on inputs and produces outputs.
main = do
 putStrLn"Please enter the input parameter"
 inputStr <- readLine
 putStrLn"Starting time:"
 getCurrentTime>> = print
 let inputInt = read inputStr -- this line is pure
 let result = fib inputInt -- this is also pure
 putStrLn"Result:"
 print result
 putStrLn"Ending time:"
 getCurrentTime>> = print

是的,纯函数可以返回时间,如果时间为参数。 不同的时间参数,不同的时间结果。 然后形成其他的时间函数,并将它们与 function(-of-time)-transforming (higher-order) 函数的简单词汇表结合起来。 因为这个方法是无状态的,所以这里的时间可以是连续的而不是离散的。 这种直觉是功能React式编程( 玻璃钢)的基础。

...