F# 什么时候应该在函数中使用函数,而不是单独的私有函数?

F# 什么时候应该在函数中使用函数,而不是单独的私有函数?,f#,F#,什么时候应该在函数中使用函数,而不是单独的私有函数 我发现我编写的函数相当长: let optionsFor piece (positions:Space list) = let yDirection = match piece with | Black _ -> -1 | Red _ -> 1 let sourceX , sourceY = match p

什么时候应该在函数中使用函数,而不是单独的私有函数

我发现我编写的函数相当长:

let optionsFor piece (positions:Space list) =

    let yDirection = match piece with
                     | Black _ -> -1
                     | Red   _ ->  1

    let sourceX , sourceY = 
        match piece with
        | Black (checker , pos) -> pos
        | Red   (checker , pos) -> pos

    let optionsForPiece = 
        (fun pos -> pos = ((sourceX - 1) , (sourceY + yDirection)) ||
                    pos = ((sourceX + 1) , (sourceY + yDirection)))

    let availableSelection = 
        (fun space -> match space with
                      | Available pos -> Some pos
                      | Allocated _   -> None)

    let availablePositions = 
        positions |> List.filter toAvailable
                  |> List.choose availableSelection

    availablePositions |> List.filter optionsForPiece
open NUnit.Framework
open FsUnit

(* Types *)
type Black = BlackKing | BlackSoldier
type Red =   RedKing   | RedSoldier

type Coordinate = int * int

type Piece =
    | Black of Black * Coordinate
    | Red   of Red   * Coordinate

type Space =
    | Allocated of Piece
    | Available of Coordinate

type Status =
    | BlacksTurn | RedsTurn
    | BlackWins  | RedWins

(* Functions *)
let black coordinate = Allocated (Black (BlackSoldier , coordinate))
let red   coordinate = Allocated (Red   (RedSoldier   , coordinate))

let startGame () =
    [ red (0,0);  red (2,0);  red (4,0);  red (6,0)
      red (1,1);  red (3,1);  red (5,1);  red (7,1)
      red (0,2);  red (2,2);  red (4,2);  red (6,2)

      Available (1,3); Available (3,3); Available (5,3); Available (7,3)
      Available (0,4); Available (2,4); Available (4,4); Available (6,4)

      black (1,5);  black (3,5);  black (5,5);  black (7,5)
      black (0,6);  black (2,6);  black (4,6);  black (6,6)
      black (1,7);  black (3,7);  black (5,7);  black (7,7) ] , BlacksTurn

let private toAvailable = 
    (fun space -> match space with
                  | Available pos -> true
                  | _             -> false)

let available (positions:Space list) = positions |> List.filter toAvailable

let optionsFor piece (positions:Space list) =

    let yDirection = match piece with
                     | Black _ -> -1
                     | Red   _ ->  1

    let sourceX , sourceY = 
        match piece with
        | Black (checker , pos) -> pos
        | Red   (checker , pos) -> pos

    let optionsForPiece = 
        (fun pos -> pos = ((sourceX - 1) , (sourceY + yDirection)) ||
                    pos = ((sourceX + 1) , (sourceY + yDirection)))

    let availableSelection = 
        (fun space -> match space with
                      | Available pos -> Some pos
                      | Allocated _   -> None)

    let availablePositions = 
        positions |> List.filter toAvailable
                  |> List.choose availableSelection

    availablePositions |> List.filter optionsForPiece
因此,我考虑将上述函数重构为几个小函数

但是,我不确定这在函数式编程中是否必要

当前关于内部函数与将其提取到私有函数的建议是什么

附录:

let optionsFor piece (positions:Space list) =

    let yDirection = match piece with
                     | Black _ -> -1
                     | Red   _ ->  1

    let sourceX , sourceY = 
        match piece with
        | Black (checker , pos) -> pos
        | Red   (checker , pos) -> pos

    let optionsForPiece = 
        (fun pos -> pos = ((sourceX - 1) , (sourceY + yDirection)) ||
                    pos = ((sourceX + 1) , (sourceY + yDirection)))

    let availableSelection = 
        (fun space -> match space with
                      | Available pos -> Some pos
                      | Allocated _   -> None)

    let availablePositions = 
        positions |> List.filter toAvailable
                  |> List.choose availableSelection

    availablePositions |> List.filter optionsForPiece
open NUnit.Framework
open FsUnit

(* Types *)
type Black = BlackKing | BlackSoldier
type Red =   RedKing   | RedSoldier

type Coordinate = int * int

type Piece =
    | Black of Black * Coordinate
    | Red   of Red   * Coordinate

type Space =
    | Allocated of Piece
    | Available of Coordinate

type Status =
    | BlacksTurn | RedsTurn
    | BlackWins  | RedWins

(* Functions *)
let black coordinate = Allocated (Black (BlackSoldier , coordinate))
let red   coordinate = Allocated (Red   (RedSoldier   , coordinate))

let startGame () =
    [ red (0,0);  red (2,0);  red (4,0);  red (6,0)
      red (1,1);  red (3,1);  red (5,1);  red (7,1)
      red (0,2);  red (2,2);  red (4,2);  red (6,2)

      Available (1,3); Available (3,3); Available (5,3); Available (7,3)
      Available (0,4); Available (2,4); Available (4,4); Available (6,4)

      black (1,5);  black (3,5);  black (5,5);  black (7,5)
      black (0,6);  black (2,6);  black (4,6);  black (6,6)
      black (1,7);  black (3,7);  black (5,7);  black (7,7) ] , BlacksTurn

let private toAvailable = 
    (fun space -> match space with
                  | Available pos -> true
                  | _             -> false)

let available (positions:Space list) = positions |> List.filter toAvailable

let optionsFor piece (positions:Space list) =

    let yDirection = match piece with
                     | Black _ -> -1
                     | Red   _ ->  1

    let sourceX , sourceY = 
        match piece with
        | Black (checker , pos) -> pos
        | Red   (checker , pos) -> pos

    let optionsForPiece = 
        (fun pos -> pos = ((sourceX - 1) , (sourceY + yDirection)) ||
                    pos = ((sourceX + 1) , (sourceY + yDirection)))

    let availableSelection = 
        (fun space -> match space with
                      | Available pos -> Some pos
                      | Allocated _   -> None)

    let availablePositions = 
        positions |> List.filter toAvailable
                  |> List.choose availableSelection

    availablePositions |> List.filter optionsForPiece

这是基于观点的,但我会给出我的观点

我的经验法则是,如果“helper”函数与“main”函数临时关联,我会将其作为嵌套函数编写。如果它们不是紧密关联的,我会将helper函数作为一个单独的函数来编写——甚至可能不会将其设置为私有函数,因为您永远不知道它何时会在不同模块中的其他代码中派上用场

紧密关联的内部函数的一个例子是一种带有累加器的循环函数,您经常在递归函数编程中编写这种循环函数。例如,下面是我为F#编程练习编写的一些代码:

module BinarySearchTree

type Node<'T> =
    { left: Node<'T> option
      value: 'T
      right: Node<'T> option }

let singleton v = { left = None; value = v; right = None }

let rec insert v t =
    if v <= t.value
        then match t.left with
             | None -> { t with left = singleton v |> Some }
             | Some n -> { t with left = insert v n |> Some }
        else match t.right with
             | None -> { t with right = singleton v |> Some }
             | Some n -> { t with right = insert v n |> Some }

let fromList l =
    match l with
    | [] -> failwith "Can't create a tree from an empty list"
    | hd::tl ->
        tl |> List.fold (fun t v -> insert v t) (singleton hd)

let toList t =
    let rec loop acc = function
        | None -> acc
        | Some node ->
            (loop [] node.left) @ (node.value :: (loop [] node.right))
    loop [] (Some t)

当您稍后重新访问代码时,可读性会大大提高,另外,在编写下一段代码时,您还可以使用更多可重用函数(如
可用的选择)。

这是基于观点的,但我将提供我的观点

我的经验法则是,如果“helper”函数与“main”函数临时关联,我会将其作为嵌套函数编写。如果它们不是紧密关联的,我会将helper函数作为一个单独的函数来编写——甚至可能不会将其设置为私有函数,因为您永远不知道它何时会在不同模块中的其他代码中派上用场

紧密关联的内部函数的一个例子是一种带有累加器的循环函数,您经常在递归函数编程中编写这种循环函数。例如,下面是我为F#编程练习编写的一些代码:

module BinarySearchTree

type Node<'T> =
    { left: Node<'T> option
      value: 'T
      right: Node<'T> option }

let singleton v = { left = None; value = v; right = None }

let rec insert v t =
    if v <= t.value
        then match t.left with
             | None -> { t with left = singleton v |> Some }
             | Some n -> { t with left = insert v n |> Some }
        else match t.right with
             | None -> { t with right = singleton v |> Some }
             | Some n -> { t with right = insert v n |> Some }

let fromList l =
    match l with
    | [] -> failwith "Can't create a tree from an empty list"
    | hd::tl ->
        tl |> List.fold (fun t v -> insert v t) (singleton hd)

let toList t =
    let rec loop acc = function
        | None -> acc
        | Some node ->
            (loop [] node.left) @ (node.value :: (loop [] node.right))
    loop [] (Some t)

当您稍后重新访问代码时,可读性会提高很多,另外,在编写下一段代码时,您还可以使用更多可重用函数(如
可用的选择
)。

我想知道这是否更适合maybe。我的代码只是用作上下文。我认为这个问题甚至不需要代码。我想我正在寻找每个选项的优缺点。我不完全确定这是否是您所要求的,但这应该取决于您的使用情况,以及您是否愿意从外部访问函数(函数、模块和其他.NET代码)。定义许多小函数和内部函数并没有什么错,只要你组织它们。优点和缺点将高度依赖于上下文……我想知道这是否更适合于maybe。我的代码只是用作上下文。我认为这个问题甚至不需要代码。我想我正在寻找每个选项的优缺点。我不完全确定这是否是您所要求的,但这应该取决于您的使用情况,以及您是否愿意从外部访问函数(函数、模块和其他.NET代码)。定义许多小函数和内部函数并没有什么错,只要你组织它们就行。优点和缺点将高度依赖于上下文。。。。