Functional programming 什么是';模式匹配';在函数语言中?
我正在阅读有关函数式编程的文章,我注意到许多文章都提到模式匹配是函数式语言的核心特性之一 有人能为Java/C++/JavaScript开发人员解释一下这意味着什么吗?你们应该从这开始,它给出了一个很好的解释。然后,阅读本手册的相关章节 这是上面维基百科中的一个很好的定义: 因此,模式匹配是一种 为事物指定名称(或绑定) 这些名字是指那些东西),以及 可能会分解表达式 同时转换为子表达式 (正如我们在列表中所做的那样 地图的定义)Functional programming 什么是';模式匹配';在函数语言中?,functional-programming,pattern-matching,terminology,Functional Programming,Pattern Matching,Terminology,我正在阅读有关函数式编程的文章,我注意到许多文章都提到模式匹配是函数式语言的核心特性之一 有人能为Java/C++/JavaScript开发人员解释一下这意味着什么吗?你们应该从这开始,它给出了一个很好的解释。然后,阅读本手册的相关章节 这是上面维基百科中的一个很好的定义: 因此,模式匹配是一种 为事物指定名称(或绑定) 这些名字是指那些东西),以及 可能会分解表达式 同时转换为子表达式 (正如我们在列表中所做的那样 地图的定义) 模式匹配是您的语言的解释器根据您提供的参数的结构和内容选择特定函
模式匹配是您的语言的解释器根据您提供的参数的结构和内容选择特定函数的地方 它不仅是一种函数式语言功能,而且可用于多种不同的语言 我第一次接触到这个想法是在我学习prolog时,它是语言的核心 e、 g 最后([LastItem],LastItem) 最后([头|尾],最后一项):- 最后(尾,最后一项) 上面的代码将给出列表的最后一项。输入参数是第一个,结果是第二个 如果列表中只有一项,解释器将选择第一个版本,第二个参数将设置为等于第一个参数,即,将为结果指定一个值
如果列表既有头也有尾,解释器将选择第二个版本并递归,直到列表中只剩下一项。对于许多人来说,如果提供一些简单的示例,则选择一个新概念会更容易,因此我们开始: 假设您有一个包含三个整数的列表,并且想要添加第一个和第三个元素。没有模式匹配,您可以这样做(Haskell中的示例): 现在,尽管这是一个玩具示例,但假设我们想将第一个和第三个整数绑定到变量并求和:
addFirstAndThird is =
let first = head is
third = is !! 3
in first + third
模式匹配就是从数据结构中提取值。您基本上是“镜像”某事物的结构,为感兴趣的地方提供绑定变量:
addFirstAndThird [first,_,third] = first + third
当您以[1,2,3]为参数调用此函数时,[1,2,3]将与[first,\uU
,third]统一,将第一个绑定到1,第三个绑定到3,并丢弃2(\uU
是您不关心的事物的占位符)
现在,如果您只想匹配第二个元素为2的列表,可以这样做:
addFirstAndThird [first,2,third] = first + third
这只适用于第二个元素为2的列表,否则会引发异常,因为没有为addFirst和third定义非匹配列表
到目前为止,我们只在解构绑定时使用模式匹配。除此之外,您可以给出同一函数的多个定义,其中使用了第一个匹配定义,因此,模式匹配有点像“立体体上的开关语句”:
addFirstAndThird将很高兴地添加列表的第一个和第三个元素,其中2作为第二个元素,否则“fall-through”和“return”为0。这种“开关式”功能不仅可用于功能定义,例如:
Prelude> case [1,3,3] of [a,2,c] -> a+c; _ -> 0
0
Prelude> case [1,2,3] of [a,2,c] -> a+c; _ -> 0
4
此外,它不限于列表,但也可以与其他类型一起使用,例如匹配Maybe类型的Just和Nothing值构造函数以“展开”值:
Prelude> case (Just 1) of (Just x) -> succ x; Nothing -> 0
2
Prelude> case Nothing of (Just x) -> succ x; Nothing -> 0
0
当然,这些仅仅是玩具的例子,我甚至没有试图给出一个正式或详尽的解释,但它们应该足以理解基本概念。这意味着,不要写作
double f(int x, int y) {
if (y == 0) {
if (x == 0)
return NaN;
else if (x > 0)
return Infinity;
else
return -Infinity;
} else
return (double)x / y;
}
你可以写
f(0, 0) = NaN;
f(x, 0) | x > 0 = Infinity;
| else = -Infinity;
f(x, y) = (double)x / y;
嘿,C++也支持模式匹配。
static const int PositiveInfinity = -1;
static const int NegativeInfinity = -2;
static const int NaN = -3;
template <int x, int y> struct Divide {
enum { value = x / y };
};
template <bool x_gt_0> struct aux { enum { value = PositiveInfinity }; };
template <> struct aux<false> { enum { value = NegativeInfinity }; };
template <int x> struct Divide<x, 0> {
enum { value = aux<(x>0)>::value };
};
template <> struct Divide<0, 0> {
enum { value = NaN };
};
#include <cstdio>
int main () {
printf("%d %d %d %d\n", Divide<7,2>::value, Divide<1,0>::value, Divide<0,0>::value, Divide<-1,0>::value);
return 0;
};
static const int PositiveInfinity=-1;
静态常量int NegativeInfinity=-2;
静态常数int NaN=-3;
模板结构划分{
枚举{value=x/y};
};
模板结构aux{enum{value=PositiveInfinity};};
模板结构aux{enum{value=NegativeInfinity};};
模板结构划分{
枚举{value=aux0)>::value};
};
模板结构划分{
枚举{value=NaN};
};
#包括
int main(){
printf(“%d%d%d%d\n”,Divide::value,Divide::value,Divide::value,Divide::value);
返回0;
};
简短回答:模式匹配之所以出现,是因为函数式语言将等号视为等价的断言,而不是赋值
详细回答:模式匹配是一种基于给定值“形状”的分派形式。在函数式语言中,您定义的数据类型通常称为有区别的并集或代数数据类型。例如,什么是(链接的)列表?某种类型A
事物的链表list
要么是空列表Nil
,要么是A
类型的某个元素Cons
插入列表A
(一个A
s的列表)。用Haskell(我最熟悉的函数式语言)编写
数据列表a=Nil
|Cons a(列表a)
所有受歧视的联合都是这样定义的:单个类型有固定数量的不同创建方法;这里的创建者,如Nil
和Cons
,称为构造函数。这意味着可以使用两个不同的构造函数创建列表a
类型的值,它可以有两种不同的形状。因此,假设我们想编写一个head
函数来获取列表的第一个元素。在Haskell中,我们将这样写
--'head'是从'List a'到'a'的函数。
标题::列表a->a
--空列表没有第一项,因此引发错误。
head Nil=错误“空列表”
--如果给我们一个“Cons”,我们只想要第一部分;这是榜首。
头部(Cons h uu)=h
由于列表a
值可以是两种不同的
f(0, 0) = NaN;
f(x, 0) | x > 0 = Infinity;
| else = -Infinity;
f(x, y) = (double)x / y;
static const int PositiveInfinity = -1;
static const int NegativeInfinity = -2;
static const int NaN = -3;
template <int x, int y> struct Divide {
enum { value = x / y };
};
template <bool x_gt_0> struct aux { enum { value = PositiveInfinity }; };
template <> struct aux<false> { enum { value = NegativeInfinity }; };
template <int x> struct Divide<x, 0> {
enum { value = aux<(x>0)>::value };
};
template <> struct Divide<0, 0> {
enum { value = NaN };
};
#include <cstdio>
int main () {
printf("%d %d %d %d\n", Divide<7,2>::value, Divide<1,0>::value, Divide<0,0>::value, Divide<-1,0>::value);
return 0;
};
switch(num) {
case 1:
// runs this when num == 1
case n when n > 10:
// runs this when num > 10
case _:
// runs this for all other cases (underscore means 'match all')
}
enum Shape {
Rectangle of { int left, int top, int width, int height }
Circle of { int x, int y, int radius }
}
switch(shape) {
case Rectangle(l, t, w, h):
// declares variables l, t, w, h and assigns properties
// of the rectangle value to the new variables
case Circle(x, y, r):
// this branch is run for circles (properties are assigned to variables)
}
function foo(a,b,c){} //no pattern matching, just a list of arguments
function foo2([a],{prop1:d,prop2:e}, 35){} //invented pattern matching in JavaScript
fibo(0) -> 0 ;
fibo(1) -> 1 ;
fibo(N) when N > 0 -> fibo(N-1) + fibo(N-2) .
function fibo(0){return 0;}
function fibo(1){return 1;}
function fibo(N) when N > 0 {return fibo(N-1) + fibo(N-2);}
type 'a list =
| Nil
| Cons of 'a * 'a list
public abstract class List<T>
{
public class Nil : List<T> { }
public class Cons : List<T>
{
public readonly T Item1;
public readonly List<T> Item2;
public Cons(T item1, List<T> item2)
{
this.Item1 = item1;
this.Item2 = item2;
}
}
}
let x = Cons(1, Cons(2, Cons(3, Cons(4, Nil))))
Stack<int> x = new Cons(1, new Cons(2, new Cons(3, new Cons(4, new Nil()))));
let peek s =
match s with
| Cons(hd, tl) -> hd
| Nil -> failwith "Empty stack"
let pop s =
match s with
| Cons(hd, tl) -> tl
| Nil -> failwith "Empty stack"
public static T Peek<T>(Stack<T> s)
{
if (s is Stack<T>.Cons)
{
T hd = ((Stack<T>.Cons)s).Item1;
Stack<T> tl = ((Stack<T>.Cons)s).Item2;
return hd;
}
else if (s is Stack<T>.Nil)
throw new Exception("Empty stack");
else
throw new MatchFailureException();
}
public static Stack<T> Pop<T>(Stack<T> s)
{
if (s is Stack<T>.Cons)
{
T hd = ((Stack<T>.Cons)s).Item1;
Stack<T> tl = ((Stack<T>.Cons)s).Item2;
return tl;
}
else if (s is Stack<T>.Nil)
throw new Exception("Empty stack");
else
throw new MatchFailureException();
}
let peek s =
match s with
| Cons(hd, tl) -> hd
| Nil -> failwith "Empty stack"
type 'a tree =
| Node of 'a tree * 'a * 'a tree
| Nil
let rotateLeft = function
| Node(a, p, Node(b, q, c)) -> Node(Node(a, p, b), q, c)
| x -> x
let rotateRight = function
| Node(Node(a, p, b), q, c) -> Node(a, p, Node(b, q, c))
| x -> x
public abstract class Tree<T>
{
public abstract U Match<U>(Func<U> nilFunc, Func<Tree<T>, T, Tree<T>, U> nodeFunc);
public class Nil : Tree<T>
{
public override U Match<U>(Func<U> nilFunc, Func<Tree<T>, T, Tree<T>, U> nodeFunc)
{
return nilFunc();
}
}
public class Node : Tree<T>
{
readonly Tree<T> Left;
readonly T Value;
readonly Tree<T> Right;
public Node(Tree<T> left, T value, Tree<T> right)
{
this.Left = left;
this.Value = value;
this.Right = right;
}
public override U Match<U>(Func<U> nilFunc, Func<Tree<T>, T, Tree<T>, U> nodeFunc)
{
return nodeFunc(Left, Value, Right);
}
}
public static Tree<T> RotateLeft(Tree<T> t)
{
return t.Match(
() => t,
(l, x, r) => r.Match(
() => t,
(rl, rx, rr) => new Node(new Node(l, x, rl), rx, rr))));
}
public static Tree<T> RotateRight(Tree<T> t)
{
return t.Match(
() => t,
(l, x, r) => l.Match(
() => t,
(ll, lx, lr) => new Node(ll, lx, new Node(lr, x, r))));
}
}
["Venice","Paris","New York","Amsterdam"]
["Venice","New York","Paris","Amsterdam"]
function up(city, cities){
for(var i = 0; i < cities.length; i++){
if(cities[i] === city && i > 0){
var prev = cities[i-1];
cities[i-1] = city;
cities[i] = prev;
}
}
return cities;
}
let up list value =
match list with
| [] -> []
| previous::current::tail when current = value -> current::previous::tail
| current::tail -> current::(up tail value)