多任务编程与系统操作实战

内容分享10小时前发布 泠冬卿
0 0 0

table {
border-collapse: collapse;
width: 100%;
margin-bottom: 1rem;
}
th, td {
border: 1px solid #ddd;
padding: 8px;
text-align: left;
}
th {
background-color: #f2f2f2;
}
tr:nth-child(even) {
background-color: #f9f9f9;
}
pre {
background-color: #f8f8f8;
padding: 15px;
border-radius: 4px;
overflow-x: auto;
}

94、编写代码将八进制数转换为十进制表示,并且可以处理用户输入的八进制数,对非八进制数给出失败提示,当无输入时安静退出

以下是使用Python语言实现的代码:


import re
while True:
    try:
        num = input()
        if not num:
            break
        if re.match(r'^[0-7]+$', num):
            decimal_num = int(num, 8)
            print(decimal_num)
        else:
            print('输入不是有效的八进制数,转换失败。')
    except EOFError:
        break

这段代码会持续等待用户输入,当用户输入为空时,程序会安静退出;若输入是有效的八进制数,会将其转换为十进制并输出;若输入不是有效的八进制数,则给出失败提示。

95、编写一个语法来匹配点分十进制IP地址,并编写动作类将其转换为对应的整数值,测试字符串为:192.168.1.137

以下是实现该功能的代码:


grammar DottedDecimal {
    token TOP { <digits> ** 4 % '.' }
    regex digits {
        ( <[0..9]> ** 3 ) <?{ 0 <= $0 <= 255 }>
    }
}

class DottedDecimal::SimpleActions {
    method TOP ($/) {
        make [+] (
            $<digits>.[0] +< 24,
            $<digits>.[1] +< 16,
            $<digits>.[2] +< 8,
            $<digits>.[3]
        );
    }
}

my $string = '192.168.1.137';
my $match = DottedDecimal.parse( $string, :actions(DottedDecimal::SimpleActions));
say $match;
say $match.made.fmt('%X');

上述代码中,

DottedDecimal

语法用于匹配点分十进制IP地址,

DottedDecimal::SimpleActions

动作类将匹配到的IP地址转换为对应的整数值。最后使用

parse

方法对测试字符串进行解析,并输出匹配结果和转换后的十六进制整数。

96、编写代码,使用Supplier来发出值,并使用tap来读取这些值

以下是示例代码:


my $supplier = Supplier.new;
my $supply = $supplier.Supply;
my $tap = $supply.tap: { put "$^a * $^a = ", $^a**2 };
$supplier.emit: 3;

上述代码中,首先创建了一个

Supplier

对象,接着通过

Supplier

对象获取

Supply

对象,然后使用

tap

方法读取值并进行处理,最后使用

emit

方法发出值。

97、要实现这个功能,你需要两样东西:一个用于发出值的 Supplier 和一个用于读取这些值的 tap。请实现这一功能。

以下是实现代码:


my $supplier = Supplier.new;
my $supply = $supplier.Supply;
my $tap = $supply.tap: { put "$^a * $^a = ", $^a**2 };
$supplier.emit: 3;

该代码先创建了一个

Supplier

对象,接着从

Supplier

获取一个

Supply

,然后使用

tap

方法来处理接收到的值,最后通过

emit

方法发出一个值

3

98、创建一个程序,该程序包含一系列步骤,每一步之间等待三秒。第一个tap在程序的大部分时间内运行,第二个tap在中间的短时间内运行。

以下是一个满足需求的Perl 6(现在称为Raku)示例代码:


my $supply = Supply.interval: 1;
# 第一个tap
$supply.tap: { say "First: $^a" };
sleep 3;
# 第二个tap
$supply.tap: { say "Second: $^a" };
sleep 3;
# 可以继续添加更多的步骤和等待时间

在这个代码中,

Supply.interval: 1

创建了一个每秒发出一个值的Supply。第一个tap立即开始运行,然后程序等待3秒。接着第二个tap开始运行,再等待3秒。第一个tap会在整个程序运行期间持续运行,而第二个tap只在中间的3秒内运行。每一步之间等待的时间是3秒。

99、创建一个简单的通道。每次循环时,它会获取一个由行号和文本组成的键值对。检查行号是否为质数,如果是,则输出该行。

可以使用以下代码实现:


my $channel = Channel.new;
$channel.Supply.tap: -> Pair:D $p {
    if $p.key.is-prime {
        put $p.value
    }
};

100、通过调用.share 可以将 Supply 转换为实时 Supply。实现一个程序来展示这种转换的效果。

以下是一个展示将 Supply 转换为实时 Supply 效果的程序示例:


my $supply = Supply.interval(0.5).share;
$supply.tap: { say "First: $^a" };
sleep 1;
$supply.tap: { say "Second: $^a" };
sleep 1;

在这个程序中,首先使用

Supply.interval(0.5)

创建一个每隔 0.5 秒发出一个值的 Supply,然后使用

.share

方法将其转换为实时 Supply。接着创建第一个 tap 来输出值,等待 1 秒后创建第二个 tap 来输出值,再等待 1 秒。运行该程序可以看到实时 Supply 的效果,即新的 tap 会从当前值开始接收,旧值在新值可用时会被丢弃。

101、在 Perl 6 的

react

块中使用

IO::Notification.watch - path

创建一个程序,用于监视文件或目录。同时,展示不同的停止程序的方法(使用

.in

Promise、间隔检查和拦截信号)。

以下是使用

IO::Notification.watch-path

监视文件或目录,并展示不同停止程序方法的代码:

基础监视程序:


sub MAIN ( Str:D $s where *.IO.e, $timeout = 10 ) {
    my $supply = IO::Notification.watch-path( $s );
    react {
        whenever $supply {
            put "{.path}: {.event}"
        }
    }
}

此程序会监视指定的文件或目录,当有变化时输出路径和事件。若指定文件则仅监视该文件,若指定目录则监视该目录及其内部文件,但不深入子目录。

使用

.in

Promise 在特定时间后停止程序:


sub MAIN ( Str:D $s where *.IO.e, $timeout = 10 ) {
    my $supply = IO::Notification.watch-path( $s );
    react {
        whenever $supply {
            put "{.path}: {.event}"
        }
        whenever Promise.in( $timeout ) {
            put "Stopping";
            done;
        }
    }
}

该程序在

$timeout

指定的时间后停止。

使用间隔检查一定时间内的变化数量来停止程序:


sub MAIN ( Str:D $s where *.IO.e ) {
    my $supply = IO::Notification.watch-path( $s );
    my $changes = 0;
    react {
        whenever $supply {
            put "{.path}: {.event}";
            $changes++
        }
        whenever Supply.interval(1) {
            if $changes > 10 {
                put 'Stopping';
                done;
            }
        }
    }
}

此程序每秒检查一次,当变化次数超过 10 次时停止。

拦截信号(如 Control-C 发送的 SIGINT)来停止程序:


sub MAIN ( Str:D $s where *.IO.e ) {
    put "PID is $*PID";
    my $supply = IO::Notification.watch-path( $s );
    my $changes = 0;
    react {
        whenever $supply {
            put "{.path}: {.event}";
            $changes++
        }
        whenever signal(SIGINT) {
            put "Interrupted! $changes changes";
            done
        }
    }
}

该程序在接收到 SIGINT 信号时停止,并输出已发生的变化次数。

102、创建一个简单的程序,根据当前操作系统选择一个字符串列表作为要运行的命令,然后运行该命令,并使用.grep 方法对命令的输出进行过滤。

以下是相应的程序代码:


my @command = $*DISTRO.is-win ?? < cmd /c dir /OS > !! < ls -lrS >;
my $proc = run @command, :out;
for $proc.out.lines.grep( rx/7/ ) -> $line {
    put $++, ': ', $line;
}

该程序首先根据当前操作系统选择命令列表,然后运行该命令,最后使用

.grep

过滤输出,仅输出包含数字

7

的行。

103、创建一个与使用

run

类似,但额外包含

.spawn

步骤的程序。

可以使用如下代码创建这样的程序:


my $is-win = $*DISTRO.is-win;
my @command = $is-win ?? < cmd /c dir > !! < ls >;

后续可根据需求对

@command

进行处理,并使用

.spawn

来运行命令。

104、这里有一个简单的程序,它根据当前操作系统选择一个字符串列表。最终存储在@command中的内容将作为run函数的参数。代码如下:my @command = $*DISTRO.is-win ?? < cmd /c dir /OS > !! < ls -lrS > ; my $proc = run @command, :out; 要过滤这些结果,可以使用.grep方法:for $proc.out.lines.grep( rx/7/ ) -> $line { put $++, ‘: ‘, $line; } 请描述该程序的功能。

该程序的功能是根据当前操作系统选择合适的命令来获取当前目录按文件大小排序的文件列表:

如果是 Windows 系统,使用命令:

cmd /c dir /OS

如果是 Unix 系统,使用命令:

ls -lrS

然后运行该命令并捕获标准输出。

最后,程序过滤输出的每一行,只输出包含数字

7

的行,并为这些行添加行号。

105、定义一个变量

$is_win

来判断是否为 Windows 系统,根据系统选择不同的命令列表

@command

,创建一个

Proc

对象

$proc

,使用

spawn

方法执行命令,并将命令的标准输出内容读取并输出。

这部分代码的功能是根据当前操作系统选择不同的命令来列出目录内容。首先判断当前系统是否为 Windows 系统,如果是则使用

cmd /c dir

命令,否则使用

ls

命令。然后创建一个

Proc

对象并使用

spawn

方法执行所选命令,最后将命令的标准输出内容读取并输出。

106、编写一个程序,将其封装在一个 MAIN 子程序中,使其能够接受命令行参数。在 react 代码块中,拦截某个命令执行的输出并统计行数。当接收到足够的输入(行数超过指定值)时,调用 done。同时添加信号处理程序(拦截 SIGINT 信号)和超时机制。

以下是实现该需求的代码:


sub MAIN ( Int:D $max-files = 100 ) {
    my $proc = Proc::Async.new: 'find', '/', '-name', '*.txt';
    react {
        my $count;
        whenever $proc.stdout.lines {
            done if ++$count > $max-files;
            put "$count: $_";
        }
        whenever signal(SIGINT) {
            put "
Interrupted! $count files";
            done;
        }
        whenever $proc.start {
            put "Finished: $count files";
            done;
        }
        whenever Promise.in(60) {
            put "Timeout: $count files";
            done;
        }
    }
}

这段代码定义了一个

MAIN

子程序,接受一个整数类型的命令行参数

$max-files

,默认值为 100。在

react

代码块中,对

find

命令的输出进行拦截和行数统计,当行数超过

$max-files

时调用

done

结束。同时添加了信号处理程序(拦截

SIGINT

信号)和超时机制(60 秒超时)。

107、编写一个 MAIN 子程序,该子程序接受一个字符串和一个数字作为参数,使用 xx 操作符将该字符串复制成包含指定数字个元素的列表。使用括号将其分组,以便可以调用 .join 方法用换行符将列表中的元素连接起来,并输出连接后的结果。


sub MAIN(Str $str, Int $n) {
    my @list = ($str xx $n);
    my $result = @list.join("
");
    say $result;
}

上述代码定义了一个

MAIN

子程序,它接受一个字符串和一个整数作为参数。使用

xx

操作符将字符串复制

$n

次形成列表,再用换行符连接列表元素并输出结果。

108、在 REPL 中尝试,(‘b5’..’f9’).List 和 (‘5b’..‘9f’).List 的结果是什么?

需在 REPL 中实际运行代码才能得到结果。在 Perl 6 的 REPL 中运行代码的方式为:运行

perl6

启动 REPL,在

>

提示符后输入:


('b5'..'f9').List


('5b'..'9f').List

并回车。

109、实现一个 count-and-show 子例程,分别传入不同的参数,观察该子例程对这些参数的处理方式

以下是一个可能的实现示例及不同参数处理的观察说明。在 Perl 6 中,可实现该子例程来统计传入参数数量并展示这些参数,示例代码如下:


sub count-and-show(*@params) {
    my $count = @params.elems;
    say "传入参数的数量: $count";
    say "传入的参数分别为: @params";
}

# 传入不同参数观察处理方式
count-and-show(1, 2, 3);
count-and-show('a', 'b', 'c', 'd');
count-and-show();

在上述代码中,定义了

count-and-show

子例程,它使用

*@params

来接收任意数量的参数。首先统计参数数量,然后展示参数数量和具体参数。通过传入不同参数(如数字、字符串、无参数)调用该子例程,可观察到它对不同情况的处理方式。

© 版权声明

相关文章

暂无评论

none
暂无评论...