Racket语言简单使用

前言

之前,我们已经看过Quick: An Introduction to Racket with Pictures。距离我们可以使用Racket编程,还有一点距离。

我们先来看看《C++ Primer》是如何介绍C++的。看过它的目录索引,或许,我们可以总结出,命令式编程语言的如下规律。

在这里插入图片描述

然而,Racket是个通用、多范型,属于Lisp家族的函数式程序设计语言。它的设计目之一是为了提供一种用于创造设计与实现其它编程语言的平台。Racket被用于脚本程序设计、通用程序设计、计算机科学教育和学术研究等不同领域。

我无法理解What is difference between functional and imperative programming languages?。所以,我暂时按照,变量-数据结构、分支/循环语句、类、模块这几部分去尝试了解Racket。目的是编程,编程,编程。所以,最后我们需要使用Racket进行简单的编程。

PS1:本文参考Racket语言入门-Racket语言概要The Racket Guide。下面的一些示例来自于这两个链接,使用的时候,不再单独说明。

PS2:在使用的过程中,理解函数式编程。


绑定-内置数据类型

绑定

Racket中似乎没有变量这一概念。我们可以使用2.2.1Definitions2.2.8 Local Binding with define, let, and let*来达到我们的目的。

(define pi 3.14)  ;defines pie to be 3
> pi
3.14

如果我们想一次绑定多个标识符,并进行运算,我们可以使用letlet绑定的范围仅仅在其作用域之内。define则是全局绑定。

(let ([a 3]
      [b (list-ref '(1 2 3 4) 3)]) ;list-ref返回位置pos处的lst元素
  (sqr (+ a b)))
  
49

> a
. . a: undefined;
 cannot reference an identifier before its definition

如果在绑定的过程中,需要互相引用,可以使用 let*

(let* ([x 1]
       [y (+ x 1)])
  (sqr (+ x y)))

9

内置数据类型

详见:3 Built-In Datatypes

常见的类型包含:布尔值,数值,字符,字符串,列表,向量,哈希表等等。

布尔值

详见:3.1 Booleans

Racket 有两个不同的常量来表示布尔值: #t表示真,# f表示假。大写 #T#F被解析为相同的值,但首选小写形式。boolean? 可以识别这两个布尔常量。

ifcondand 这样的语句中,任何其他值,而不知#f均为true。

(>= 2 (+ 1 1)) ;#t
(boolean? #t) ;#t
(boolean? #f)

数值

详见:3.2 Numbers

Racket的数值类型可以分为两种:精确和不精确。

精确的数字:任意大小的整数、一个正好是两个任意大小的整数之比的有理数、具有精确实部和虚部(其中虚部不为零)的复数

不精确的数字:数字的 IEEE 浮点表示、具有实部和虚部的复数是 IEEE 浮点表示

(sin 1/2)
0.479425538604203

语句

条件语句

详见:2.2.5 Conditionals with if, and, or, and cond4.7 Conditionals

(if (> 2 3)
    "2 bigger than 3"
    "2 not bigger than 3")

"2 not bigger than 3"

Racket的 if没有多个分支,但是可以通过嵌套来完成相同的功能:

(define score 55)
(if (>= score 90) "A"
    (if (>= score 80) "B"
        (if(>= score 70) "C"
           (if(>= score 60) "D"
              "Not Pass"))))

"Not Pass"

同样的功能可以 cond 来完成:

(define score 55)
(cond [(>= score 90) "A"]
      [(>= score 80) "B"]
      [(>= score 70) "C"]
      [(>= score 60) "D"]
      [else "Not Pass"])

"Not Pass"

循环语句

详见:2.3Lists, Iteration, and Recursion11 Iterations and Comprehensions

对列表中每个元素平方刷出。

(for ([i '(1 2 3)])
  (let ([tmp (* i i)])
    (printf "~a " tmp)))

1 4 9 

同时遍历多个列表。

(for ([i '(1 2 3 4)]
           [name '("goodbye" "farewell" "so long")]) ; 同时遍历,一个停止,全部停止
  (printf "~a: ~a\n" i name))

1: goodbye
2: farewell
3: so long

双层循环

;; 双层循环
(for* ([i '(1 2 3 4)]
           [name '("goodbye" "farewell" "so long")])
  (printf "~a: ~a\n" i name))

1: goodbye
1: farewell
1: so long
2: goodbye
2: farewell
2: so long
3: goodbye
3: farewell
3: so long
4: goodbye
4: farewell
4: so long

函数

详见:2.2.4 Function Calls (Procedure Applications)2.2.7 Anonymous Functions with lambda4.5.1 Function Shorthand

调用string-append函数,进行字符串拼接。

(string-append "hello " "world")

"hello world"

使用lambda表达式,创建匿名函数。

; (map proc lst ...+) → list? ;对list中的每个元素,应用proc函数
(map
 (lambda (num) (+ 1 num))
 '(1 2 3))

'(2 3 4)

创建一个函数。

(define (salutation)
  (list-ref '("Hi" "Hello") (random 2)))
(define (greet name)
  (string-append (salutation) "," name))

> (greet "Tom and jerry")
"Hello,Tom and jerry"
> (greet "Tom and jerry")
"Hi,Tom and jerry"

类和对象

详见:13 类和对象 --> 翻译

; 创建一个 fish% 类(%是给类绑定用的)
(define fish%
  (class object% ; 其他类都是从object%派生的
    (init size) ; 初始化的参数
    (super-new) ; 父类的初始化
    ;; 域(fields)
    (define current-size size)
    ;; 公共方法
    (define/public (get-size)
      current-size)
    (define/public (grow amt)
      (set! current-size (+ amt current-size)))
    (define/public (eat other-fish)
      (grow (send other-fish get-size)))))

;; 创建一个 fish% 类的示例
(define charlie
  (new fish% [size 10]))

;; 使用 `send' 调用一个对象的方法
(send charlie get-size) ; => 10
(send charlie grow 6)
(send charlie get-size) ; => 16

模块

详见:6 Modules

将下面内容保存在"cack.rkt"中。

#lang racket
 
(provide print-cake)
 
; draws a cake with n candles
(define (print-cake n)
  (show "   ~a   " n #\.)
  (show " .-~a-. " n #\|)
  (show " | ~a | " n #\space)
  (show "---~a---" n #\-))
 
(define (show fmt n ch)
  (printf fmt (make-string n ch))
  (newline))

在另一个文件中,调用print-cake函数。

(require "cake.rkt")
(print-cake (random 6))

杨辉三角

题目:118. 杨辉三角

参考题解:杨辉三角-力扣官方题解
在这里插入图片描述

C++ 实现

class Solution {
public:
    vector<vector<int>> generate(int numRows) {
        // 除去边缘数字,满足公式dp[i][j] = dp[i-1][j] + dp[i-1][j-1]
       vector<vector<int>> dp(numRows);

        for(int i=0; i<numRows; i++){
            int n = i+1; // 该行有i+1个数字
            dp[i].resize(n);
            dp[i][0] = 1; // 边缘数字初始化为零
            dp[i][n-1] = 1;
            
            for(int j=1; j<n-1; j++){ // 非边缘数字满足上面公式
                dp[i][j] = dp[i-1][j] + dp[i-1][j-1];
            }
        }

        return dp;
    }
};

Racket实现一

代码主体来源:Racket-wiki。下面代码可以在leetcode上通过。

#lang racket

; 在leetcode中实现上面的代码
(require racket/contract)

(define (next-row row) ;从杨辉三角的一行生成下一行:当前行左边添加一个0,成为新的序列A;当前行右边添加一个0,生成新的序列B;A+B为下一行的杨辉三角
  (map + (cons 0 row) (append row '(0))))

(define (triangle row rows)
  (if (= rows 0)
      '()
      (cons row (triangle (next-row row) (- rows 1))))) ; 用递归实现循环

(define/contract (generate numRows)
  (-> exact-integer? (listof (listof exact-integer?))) ;定义域(接收)和值域(产生)为,确切的正整数,二维确切正整数列表
  (triangle (list 1) numRows))

> (generate 5)
'((1) (1 1) (1 2 1) (1 3 3 1) (1 4 6 4 1))

如果不明白上面的递归展开过程。自行尝试展开,即可明白。

; 第一次展开
(define (triangle (1) 5)
  (if (= 5 0)
      '()
      (cons (1) (triangle (next-row (1)) (- 5 1))))) ;第一组(1)

(define (next-row (1))
  (map + (cons 0 (1)) (append (1) '(0)))) ;(1 1)


; 第二次展开
(define (triangle (1 1) 4)
  (if (= 4 0)
      '()
      (cons (1 1) (triangle (next-row (1 1)) (- 4 1))))) ;第二组(1 1)

(define (next-row (1 1))
  (map + (cons 0 (1 1)) (append (1 1) '(0)))) ;(1 2 1)

Racket实现二

我尝试使用for循环代替上面的递归过程。

#lang racket

; 在leetcode中实现上面的代码
(require racket/contract)
;(require flomat)

(define (next-row row) ;从杨辉三角的一行生成下一行:当前行左边添加一个0,成为新的序列A;当前行右边添加一个0,生成新的序列B;A+B为下一行的杨辉三角
  (map + (cons 0 row) (append row '(0))))

(define (triangle rows)
  (let ([ret '((1))] [tmp '(1)])
  (for ([i (- rows 1)])
      (set! tmp (next-row tmp))
      (set! ret (append ret tmp)))
  ret))


(define/contract (generate numRows)
  (-> exact-integer? (listof (listof exact-integer?))) ;定义域(接收)和值域(产生)为,确切的正整数,二维确切正整数列表
  (triangle numRows))

这个代码是有问题的。问题在于,racket中如何在二维数组中追加一个一维数组作为元素。

> (triangle 5)
'((1) 1 1 1 2 1 1 3 3 1 1 4 6 4 1)

看完4.10Pairs and Lists4.12Vectors,我没有看到这两个内置数据结构的方法,可以实现上面的功能。Racket中有矩阵库flomat,类似于numpy,不知道能不能解决这个问题?我用了一上午没有解决这个问题,暂时搁置。

问题总结:Racket中,如何将一维的列表,追加到二维列表后面?

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

da1234cao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值