通过游戏开发学习 F# 第 3 章错误
Learning F# Through Game Dev chapter 3 error
我正在阅读《通过游戏开发学习 F#》一书,在第 3 章中我得到了一个带有以下代码的 TypeInitializationException:
// edit: missing definitions, thanks John
let earth_mass = 5.97e24<kg> // defined in other file that is ref'd in ok
let moon_mass = 7.35e22<kg>
let lerp (x:float<'T>) (y:float<'T>) (a:float) = (x * a) + (y * (1.0 - a))
// inside list comprehension for loop -
let m = (lerp earth_mass moon_mass (rand.NextDouble())) * 1.0e-4 // error!!
我已经下载了作者的源代码,看看我是否遗漏了什么,但它产生了同样的错误。
异常消息几乎没有提供问题的线索(至少对我而言)。
下面是本章的全部代码,供参考:
namespace Games
module Chapter3 =
open System
open System.Threading
open Games.Math
type Asteroid =
{
Position : Vector2<m>
Velocity : Vector2<m/s>
Mass : float<kg>
Name : string
}
let dt = 60.0<s>
let G = 6.67e-11<m^3 * kg^-1 * s^-2>
let earth_radius = 6.37e6<m>
let field_size = earth_radius * 60.0
let max_velocity = 2.3e4<m/s>
let earth_mass = 5.97e24<kg>
let moon_mass = 7.35e22<kg>
let create_field num_asteroids =
let lerp (x:float<'T>) (y:float<'T>) (a:float) = x * a + y * (1.0 - a)
let rand = Random()
[
for i = 1 to num_asteroids do
let m = (lerp earth_mass moon_mass (rand.NextDouble())) * 1.0e-4
let x = lerp 0.0<m> field_size (rand.NextDouble())
let y = lerp 0.0<m> field_size (rand.NextDouble())
let vx = max_velocity * (rand.NextDouble() * 2.0 - 1.0) * 0.1
let vy = max_velocity * (rand.NextDouble() * 2.0 - 1.0) * 0.1
yield
{
Position = { X = x; Y = y}
Velocity = { X = vx; Y = vy}
Mass = m
Name = "a"
}
]
let f0 = create_field 20
let clamp (p:Vector2<_>, v:Vector2<_>) =
let p,v =
if p.X < 0.0<_> then
{p with X = 0.0<_>}, {v with X = -v.X}
else
p,v
let p,v =
if p.X > field_size then
{p with X = field_size}, {v with X = -v.X}
else
p,v
let p,v =
if p.Y < 0.0<_> then
{p with Y = 0.0<_>}, {v with Y = -v.Y}
else
p,v
let p,v =
if p.Y > field_size then
{p with Y = field_size}, {v with Y = -v.Y}
else
p,v
p,v
let force (a:Asteroid, a':Asteroid) =
let dir = a'.Position - a.Position
let dist = dir.Length + 1.0<m>
G * a.Mass * a'.Mass * dir/(dist * dist * dist)
let simulation_step (asteroids:Asteroid list) =
[
for a in asteroids do
let forces =
[
for a' in asteroids do
if a' <> a then
yield force(a, a')
]
let F = List.sum forces
let p', v' = clamp(a.Position, a.Velocity)
yield
{
a with
Position = p' + dt * v'
Velocity = v' + dt * F/a.Mass
}
]
let print_scene (asteroids:Asteroid list) =
do Console.Clear()
for i = 0 to 79 do
Console.SetCursorPosition(i, 0)
Console.Write("*")
Console.SetCursorPosition(i, 23)
Console.Write("*")
for j = 0 to 23 do
Console.SetCursorPosition(0, j)
Console.Write("*")
Console.SetCursorPosition(79, j)
Console.Write("*")
let set_cursor_on_body b =
Console.SetCursorPosition(
((b.Position.X/4.0e8<m>) * 78.0 + 1.0) |> int,
((b.Position.Y/4.0e8<m>) * 23.0 + 1.0) |> int)
for a in asteroids do
do set_cursor_on_body a
do Console.Write(a.Name)
do Thread.Sleep(100)
let simulation() =
let rec simulation m =
do print_scene m
let m' = simulation_step m
do simulation m'
do simulation f0
数学模块代码:
namespace Games
module Math =
[<Measure>]
type m //metres
[<Measure>]
type kg //kilogram
[<Measure>]
type s // seconds
[<Measure>]
type N = kg*m/s^2 //Newtons
type Vector2<[<Measure>]'T> =
{
X : float<'T>
Y : float<'T>
}
static member Zero : Vector2<'T> =
{ X = 0.0<_>; Y = 0.0<_> }
static member (+) (v1:Vector2<'T>, v2:Vector2<'T>) : Vector2<'T> =
{ X = v1.X + v2.X; Y = v1.Y + v2.Y }
static member (+) (v:Vector2<'T>, k:float<'T>) : Vector2<'T> =
{ X = v.X + k; Y = v.Y + k }
static member (+) (k:float<'T>, v:Vector2<'T>) : Vector2<'T> = v + k
static member (~-) (v:Vector2<'T>) : Vector2<'T> =
{ X = -v.X; Y = -v.Y }
static member (-) (v1:Vector2<'T>, v2:Vector2<'T>) : Vector2<'T> =
v1 + (-v2)
static member (-) (v:Vector2<'T>, k:float<'T>) : Vector2<'T> =
v + (-k)
static member (-) (k:float<'T>, v:Vector2<'T>) : Vector2<'T> =
k + (-v)
static member (*) (v1:Vector2<'a>, v2:Vector2<'b>) : Vector2<'a * 'b> =
{ X = v1.X * v2.X; Y = v1.Y * v2.Y }
static member (*) (v:Vector2<'a>, f:float<'b>) : Vector2<'a * 'b> =
{ X = v.X * f; Y = v.Y * f }
static member (*) (f:float<'b>, v:Vector2<'a>) : Vector2<'b * 'a> =
{ X = f * v.X; Y = f * v.Y }
static member (/) (v:Vector2<'a>, f:float<'b>) : Vector2<'a / 'b> =
v * (1.0/f)
member this.Length : float<'a> =
sqrt((this.X * this.X + this.Y * this.Y))
static member Distance(v1:Vector2<'T>, v2:Vector2<'T>) =
(v1 - v2).Length
static member Normalize(v:Vector2<'T>) : Vector2<1> =
v / v.Length
运行 示例的代码:
[<EntryPoint>]
let main argv =
Games.Chapter3.simulation()
0 // return an integer exit code
有人可以使用 VS2013 和 F# 3.1/.Net4.5 解释发生了什么吗
谢谢。
米克
相信您 运行 陷入 this F# 编译器错误。它存在于 F# 3.1.2 及更早版本中,但此后已修复,因此在 4.0 及更高版本中这将起作用。
根据问题讨论,在 F# 3.0 和更早版本中存在错误,但并不总是导致运行时崩溃。查看本书的示例代码项目,我猜作者使用 F# 3.0 或更早版本编写这些示例,并没有意识到它们会触发错误。
解决方法:
- 在 Release 模式下编译(启用优化),codegen 应该是正确的(假设这确实是同一个问题)
- 尝试使用 F# 4.0 预发布版(通过 VS 2015 CTPs or grab just the F# bits from here)
我正在阅读《通过游戏开发学习 F#》一书,在第 3 章中我得到了一个带有以下代码的 TypeInitializationException:
// edit: missing definitions, thanks John
let earth_mass = 5.97e24<kg> // defined in other file that is ref'd in ok
let moon_mass = 7.35e22<kg>
let lerp (x:float<'T>) (y:float<'T>) (a:float) = (x * a) + (y * (1.0 - a))
// inside list comprehension for loop -
let m = (lerp earth_mass moon_mass (rand.NextDouble())) * 1.0e-4 // error!!
我已经下载了作者的源代码,看看我是否遗漏了什么,但它产生了同样的错误。 异常消息几乎没有提供问题的线索(至少对我而言)。
下面是本章的全部代码,供参考:
namespace Games
module Chapter3 =
open System
open System.Threading
open Games.Math
type Asteroid =
{
Position : Vector2<m>
Velocity : Vector2<m/s>
Mass : float<kg>
Name : string
}
let dt = 60.0<s>
let G = 6.67e-11<m^3 * kg^-1 * s^-2>
let earth_radius = 6.37e6<m>
let field_size = earth_radius * 60.0
let max_velocity = 2.3e4<m/s>
let earth_mass = 5.97e24<kg>
let moon_mass = 7.35e22<kg>
let create_field num_asteroids =
let lerp (x:float<'T>) (y:float<'T>) (a:float) = x * a + y * (1.0 - a)
let rand = Random()
[
for i = 1 to num_asteroids do
let m = (lerp earth_mass moon_mass (rand.NextDouble())) * 1.0e-4
let x = lerp 0.0<m> field_size (rand.NextDouble())
let y = lerp 0.0<m> field_size (rand.NextDouble())
let vx = max_velocity * (rand.NextDouble() * 2.0 - 1.0) * 0.1
let vy = max_velocity * (rand.NextDouble() * 2.0 - 1.0) * 0.1
yield
{
Position = { X = x; Y = y}
Velocity = { X = vx; Y = vy}
Mass = m
Name = "a"
}
]
let f0 = create_field 20
let clamp (p:Vector2<_>, v:Vector2<_>) =
let p,v =
if p.X < 0.0<_> then
{p with X = 0.0<_>}, {v with X = -v.X}
else
p,v
let p,v =
if p.X > field_size then
{p with X = field_size}, {v with X = -v.X}
else
p,v
let p,v =
if p.Y < 0.0<_> then
{p with Y = 0.0<_>}, {v with Y = -v.Y}
else
p,v
let p,v =
if p.Y > field_size then
{p with Y = field_size}, {v with Y = -v.Y}
else
p,v
p,v
let force (a:Asteroid, a':Asteroid) =
let dir = a'.Position - a.Position
let dist = dir.Length + 1.0<m>
G * a.Mass * a'.Mass * dir/(dist * dist * dist)
let simulation_step (asteroids:Asteroid list) =
[
for a in asteroids do
let forces =
[
for a' in asteroids do
if a' <> a then
yield force(a, a')
]
let F = List.sum forces
let p', v' = clamp(a.Position, a.Velocity)
yield
{
a with
Position = p' + dt * v'
Velocity = v' + dt * F/a.Mass
}
]
let print_scene (asteroids:Asteroid list) =
do Console.Clear()
for i = 0 to 79 do
Console.SetCursorPosition(i, 0)
Console.Write("*")
Console.SetCursorPosition(i, 23)
Console.Write("*")
for j = 0 to 23 do
Console.SetCursorPosition(0, j)
Console.Write("*")
Console.SetCursorPosition(79, j)
Console.Write("*")
let set_cursor_on_body b =
Console.SetCursorPosition(
((b.Position.X/4.0e8<m>) * 78.0 + 1.0) |> int,
((b.Position.Y/4.0e8<m>) * 23.0 + 1.0) |> int)
for a in asteroids do
do set_cursor_on_body a
do Console.Write(a.Name)
do Thread.Sleep(100)
let simulation() =
let rec simulation m =
do print_scene m
let m' = simulation_step m
do simulation m'
do simulation f0
数学模块代码:
namespace Games
module Math =
[<Measure>]
type m //metres
[<Measure>]
type kg //kilogram
[<Measure>]
type s // seconds
[<Measure>]
type N = kg*m/s^2 //Newtons
type Vector2<[<Measure>]'T> =
{
X : float<'T>
Y : float<'T>
}
static member Zero : Vector2<'T> =
{ X = 0.0<_>; Y = 0.0<_> }
static member (+) (v1:Vector2<'T>, v2:Vector2<'T>) : Vector2<'T> =
{ X = v1.X + v2.X; Y = v1.Y + v2.Y }
static member (+) (v:Vector2<'T>, k:float<'T>) : Vector2<'T> =
{ X = v.X + k; Y = v.Y + k }
static member (+) (k:float<'T>, v:Vector2<'T>) : Vector2<'T> = v + k
static member (~-) (v:Vector2<'T>) : Vector2<'T> =
{ X = -v.X; Y = -v.Y }
static member (-) (v1:Vector2<'T>, v2:Vector2<'T>) : Vector2<'T> =
v1 + (-v2)
static member (-) (v:Vector2<'T>, k:float<'T>) : Vector2<'T> =
v + (-k)
static member (-) (k:float<'T>, v:Vector2<'T>) : Vector2<'T> =
k + (-v)
static member (*) (v1:Vector2<'a>, v2:Vector2<'b>) : Vector2<'a * 'b> =
{ X = v1.X * v2.X; Y = v1.Y * v2.Y }
static member (*) (v:Vector2<'a>, f:float<'b>) : Vector2<'a * 'b> =
{ X = v.X * f; Y = v.Y * f }
static member (*) (f:float<'b>, v:Vector2<'a>) : Vector2<'b * 'a> =
{ X = f * v.X; Y = f * v.Y }
static member (/) (v:Vector2<'a>, f:float<'b>) : Vector2<'a / 'b> =
v * (1.0/f)
member this.Length : float<'a> =
sqrt((this.X * this.X + this.Y * this.Y))
static member Distance(v1:Vector2<'T>, v2:Vector2<'T>) =
(v1 - v2).Length
static member Normalize(v:Vector2<'T>) : Vector2<1> =
v / v.Length
运行 示例的代码:
[<EntryPoint>]
let main argv =
Games.Chapter3.simulation()
0 // return an integer exit code
有人可以使用 VS2013 和 F# 3.1/.Net4.5 解释发生了什么吗
谢谢。
米克
相信您 运行 陷入 this F# 编译器错误。它存在于 F# 3.1.2 及更早版本中,但此后已修复,因此在 4.0 及更高版本中这将起作用。
根据问题讨论,在 F# 3.0 和更早版本中存在错误,但并不总是导致运行时崩溃。查看本书的示例代码项目,我猜作者使用 F# 3.0 或更早版本编写这些示例,并没有意识到它们会触发错误。
解决方法:
- 在 Release 模式下编译(启用优化),codegen 应该是正确的(假设这确实是同一个问题)
- 尝试使用 F# 4.0 预发布版(通过 VS 2015 CTPs or grab just the F# bits from here)