【问题标题】:Cannot properly encode a sample text using huffman无法使用霍夫曼正确编码示例文本
【发布时间】:2017-10-27 09:31:54
【问题描述】:

问题出现在find函数中,erlang shell告诉我发生了异常错误,它说:

Exception error: no function clause matching seminar1:find("t", []) (seminar1.erl, line 117) in function seminar1:encode/3 ( seminar1.erl, line 113).

我相信发生的事情是在第一个 find 函数中完成的模式匹配总是失败,尽管我不明白为什么,因为手动进行比较的尝试已经成功。

-module(seminar1).
-compile(export_all).

sample() -> "the quick brown fox jumps over the lazy dog
this is a sample text that we will use when we build
up a table we will only handle lower case letters and
no punctuation symbols the frequency will of course not
represent english but it is probably not that far off".

text() -> "this is something that we should encode".

test() ->
Sample = sample(),
Tree = tree(Sample),
Encode = encode_table(Tree),
Decode = decode_table(Tree),
Text = text(),
Seq = encode(Text, Encode),
Text = decode(Seq, Decode).


tree(Sample) -> Freq = freq(Sample),

            F = fun({node,N1,V1,_,_}, {node,N2,V2,_,_}) -> 
                    if
                        V1 > V2 -> false;
                        V1 == V2 -> if
                                    N1 > N2 -> false;
                                    true -> true
                                    end;
                        true -> true
                    end 
                end,
            %lists:sort(F,Freq).
            huffman(lists:sort(F,Freq)).


% Calculate the frequency of each letter in the Sample and return a 
datastructure of nodes containing the letter involved,
% frequency of it in the sample.
% datastructure {node, Key, Value, Left, Right}
freq(Sample) -> freq(Sample, []).

freq([], Freq) -> Freq;
freq([Char|Rest], Freq) ->  freq(Rest, check(Char, Freq)).

% Check function complements the Freq function, it takes the current input 
and pattern matches it with the frequency datastructue being built.
% If it scores a hit that particular node has its frequency incremented and 
then the whole datastructure is returned.
check(Key, []) -> [{node, [Key], 1, nil, nil}];
check(Key, [{node, [Key], Value, nil, nil}| Tail]) -> [{node, [Key], Value + 
1, nil, nil}| Tail];
check(Key, [H|T]) -> [H |check(Key, T)].

% Creates the Huffman tree that is later used to encode a sample.
% The input is the SORTED datastructure derived from the freq-function.
% The leaves of the huffman tree are where actual values reside, branches 
are just nodes containing information.
huffman( [ Tree | [] ] ) -> Tree;

huffman([{node, LeftKey, LeftValue, _L1, _R1}, 
     {node, RightKey, RightValue, _L2, _R2} |Tail]) ->

    % Creating a branch node
    BranchNode = {node, LeftKey ++ RightKey, LeftValue + RightValue, {node, 
LeftKey, LeftValue, _L1, _R1}, {node, RightKey, RightValue, _L2, _R2}},

    huffman(insert(BranchNode, Tail)).

% A complementary function to the huffman function, inserts the newly made 
branchnode into the already sorted tail.
% This is to prevent the sorted tail from becoming unsorted when turning the 
tail list into a tree.
% It is inserted as such that the branchnode is the first selection of its 
current value, 
% meaning that if you have 4 nodes of value 5 ( one being a branchnode) then 
the branch node will be the first option.
% This will make the Tree structure left leaning.
%
%             N
%         N       N
%      N    N   
%     N N   N N
insert(Node, []) -> [Node|[]];

insert(Node, [H|T]) ->
    {_, _, Nvalue, _, _} = Node,
    {_, _, Hdvalue, _, _} = H,
    if
        Nvalue =< Hdvalue -> [ Node | [H|T]];
        true -> [H | insert(Node, T)]
    end.



% Takes the tree created by the huffman-function as input and traverses said 
tree.
% Returns a list containing the letters found and their position in the 
tree, Left = 0, Right = 1.
% {"e"/[101], [0,0,0]} -- {[Key], [pathway]}
% Traversal method used: Left based traversal.
encode_table(RootNode) -> encode_table(RootNode, [], []).

% When traversing the Tree I need to know the branchnode I am in, the result 
list as I am adding letters to it and a PathwayList which is the current 
binary path to the branchnode I am in.
encode_table({_, Key, _, nil, nil}, AccList, PathwayList) ->
    [AccList | [{Key, reverse(PathwayList)}]];
encode_table({_, _, _, Left, Right}, AccList, PathwayList) ->
    encode_table(
        Right, 
        encode_table(Left, AccList, [0| PathwayList]), 
        [1|PathwayList]).

% Complementary function for the encode_table/3 function, when traversing 
the tree the the pathway gets reversed so it needs to be corrected.
reverse(L) -> reverse(L, []).
reverse([], Rev) -> Rev;
reverse([H|T], Rev) -> reverse(T, [H|Rev]).


% Takes a sample text and encodes it in accordance to the encoding table 
supplied
encode(Text, Table) -> encode(Text, Table, []).

encode([], _, EncodedText) -> EncodedText;
encode([Letter|Rest], Table, EncodedText) ->
    encode(Rest, Table, [find([Letter], Table) | EncodedText]).

% Complementary function to encode/3, searches the Table for the related 
Letters binary path.
%find(Letter, []) -> Letter;
find(Letter, [{Letter, BinaryPath} | _Rest]) -> 
    BinaryPath;
find(Letter, [ _ | Rest]) -> 
    find(Letter, Rest).

decode_table(tree) -> ok.
decode(sequence, table) -> ok.

test(Letter, [{Letter, Asd} | []]) ->
    true;
test(_, _) -> false.

【问题讨论】:

  • 要分析的代码很多,但是如果取消注释%find(Letter, []) -&gt; Letter;这一行,至少错误会消失
  • 嗯,是的,这是真的!但是它并不能解决我遇到的问题,即“find(Letter, [{Letter, BinaryPath} | _Rest]) -> BinaryPath;”的模式匹配。一直失败导致程序只遍历表直到最后。

标签: erlang pattern-matching


【解决方案1】:

我已尝试遵循您的代码,但我被困在函数 decode_table(tree) -&gt; ok. 上。使用这种拼写,它会失败(tree 是一个原子,除了tree 本身之外不会匹配任何东西)。当我了解到尚未编写或未提供解码功能时,请更改为 _Tree 忽略此问题。

对于编码,麻烦的是函数encode_table返回一个嵌套列表,不适合find函数。如果你用encode_table(RootNode) -&gt; lists:flatten(encode_table(RootNode, [], [])). 替换代码,那么它就可以工作(至少它似乎可以工作,因为我不知道你期待什么结果)

-module(seminar1).
-compile(export_all).

sample() -> "the quick brown fox jumps over the lazy dog
this is a sample text that we will use when we build
up a table we will only handle lower case letters and
no punctuation symbols the frequency will of course not
represent english but it is probably not that far off".

text() -> "this is something that we should encode".

test() ->
Sample = sample(),
Tree = tree(Sample),
Encode = encode_table(Tree),
%Decode = decode_table(Tree),
Text = text(),
Seq = encode(Text, Encode),
%Text = decode(Seq, Decode).
Seq.

tree(Sample) -> Freq = freq(Sample),

            F = fun({node,N1,V1,_,_}, {node,N2,V2,_,_}) ->
                    if
                        V1 > V2 -> false;
                        V1 == V2 -> if
                                    N1 > N2 -> false;
                                    true -> true
                                    end;
                        true -> true
                    end
                end,
            %lists:sort(F,Freq).
            huffman(lists:sort(F,Freq)).


% Calculate the frequency of each letter in the Sample and return a
% datastructure of nodes containing the letter involved,
% frequency of it in the sample.
% datastructure {node, Key, Value, Left, Right}
freq(Sample) -> freq(Sample, []).

freq([], Freq) -> Freq;
freq([Char|Rest], Freq) ->  freq(Rest, check(Char, Freq)).

% Check function complements the Freq function, it takes the current input
% and pattern matches it with the frequency datastructue being built.
% If it scores a hit that particular node has its frequency incremented and
% then the whole datastructure is returned.
check(Key, []) ->
    [{node, [Key], 1, nil, nil}];
check(Key, [{node, [Key], Value, nil, nil}| Tail]) ->
    [{node, [Key], Value + 1, nil, nil}| Tail];
check(Key, [H|T]) ->
    [H |check(Key, T)].

% Creates the Huffman tree that is later used to encode a sample.
% The input is the SORTED datastructure derived from the freq-function.
% The leaves of the huffman tree are where actual values reside, branches
% are just nodes containing information.
huffman( [ Tree | [] ] ) -> Tree;

huffman([{node, LeftKey, LeftValue, _L1, _R1},
         {node, RightKey, RightValue, _L2, _R2} |Tail]) ->

    % Creating a branch node
    BranchNode = {node, LeftKey ++ RightKey, LeftValue + RightValue, {node, LeftKey, LeftValue, _L1, _R1}, {node, RightKey, RightValue, _L2, _R2}},

    huffman(insert(BranchNode, Tail)).

% A complementary function to the huffman function, inserts the newly made
% branchnode into the already sorted tail.
% This is to prevent the sorted tail from becoming unsorted when turning the
% tail list into a tree.
% It is inserted as such that the branchnode is the first selection of its
% current value,
% meaning that if you have 4 nodes of value 5 ( one being a branchnode) then
% the branch node will be the first option.
% This will make the Tree structure left leaning.
%
%             N
%         N       N
%      N    N
%     N N   N N
insert(Node, []) -> [Node|[]];

insert(Node, [H|T]) ->
    {_, _, Nvalue, _, _} = Node,
    {_, _, Hdvalue, _, _} = H,
    if
        Nvalue =< Hdvalue -> [ Node | [H|T]];
        true -> [H | insert(Node, T)]
    end.



% Takes the tree created by the huffman-function as input and traverses said tree.
% Returns a list containing the letters found and their position in the
% tree, Left = 0, Right = 1.
% {"e"/[101], [0,0,0]} -- {[Key], [pathway]}
% Traversal method used: Left based traversal.
encode_table(RootNode) -> lists:flatten(encode_table(RootNode, [], [])).

% When traversing the Tree I need to know the branchnode I am in, the result
% list as I am adding letters to it and a PathwayList which is the current
% binary path to the branchnode I am in.
encode_table({_, Key, _, nil, nil}, AccList, PathwayList) ->
    [AccList | [{Key, reverse(PathwayList)}]];
encode_table({_, _, _, Left, Right}, AccList, PathwayList) ->
    encode_table(
        Right,
        encode_table(Left, AccList, [0| PathwayList]),
        [1|PathwayList]).

% Complementary function for the encode_table/3 function, when traversing
% the tree the the pathway gets reversed so it needs to be corrected.
reverse(L) -> reverse(L, []).
reverse([], Rev) -> Rev;
reverse([H|T], Rev) -> reverse(T, [H|Rev]).


% Takes a sample text and encodes it in accordance to the encoding table supplied
encode(Text, Table) -> encode(Text, Table, []).

encode([], _, EncodedText) -> EncodedText;
encode([Letter|Rest], Table, EncodedText) ->
    encode(Rest, Table, [find([Letter], Table) | EncodedText]).

% Complementary function to encode/3, searches the Table for the related
% Letters binary path.
find(Letter, []) -> Letter;
find(Letter, [{Letter, BinaryPath} | _Rest]) ->
    BinaryPath;
find(Letter, [ _ | Rest]) ->
    find(Letter, Rest).

decode_table(_Tree) -> ok.
decode(sequence, table) -> ok.

test(Letter, [{Letter, _Asd} | []]) ->
    true;
test(_, _) -> false.

给出结果:

64> c(seminar1).             
{ok,seminar1}
65> rp(seminar1:test()).
[[0,0,0],
 [1,0,0,0,1,0],
 [0,1,1,1],
 [1,0,1,1,0,0],
 [0,1,0,0],
 [0,0,0],
 [1,1,1],
 [1,0,0,0,1,0],
 [1,0,0,1],
 [1,1,0,1,0],
 [0,1,1,1],
 [1,0,1,0,0],
 [0,1,0,1],
 [1,1,1],
 [0,0,0],
 [1,0,1,0,1],
 [1,1,1],
 [1,1,0,0],
 [0,0,1,1],
 [1,0,1,0,0],
 [1,1,0,0],
 [1,1,1],
 [1,0,0,0,0,0,0],
 [0,1,0,0],
 [1,1,0,1,1],
 [1,0,1,0,0],
 [1,1,0,0],
 [0,0,0],
 [1,0,0,0,1,1,1],
 [0,1,1,1],
 [0,1,0,1],
 [1,1,1],
 [0,1,0,1],
 [1,1,0,1,1],
 [1,1,1],
 [0,1,0,1],
 [1,1,0,1,1],
 [1,0,1,0,0],
 [1,1,0,0]]
ok
66>

编辑

如果你替换,你会得到同样的结果

encode_table({_, Key, _, nil, nil}, AccList, PathwayList) ->
    [AccList | [{Key, reverse(PathwayList)}]];

它负责这个版本的嵌套结果,直接产生一个平面列表。

encode_table({_, Key, _, nil, nil}, AccList, PathwayList) ->
    [{Key, reverse(PathwayList)} | AccList];

这是构建列表的一般方法:[Head|Tail] 其中Head 是任何erlang 术语,Tail 是列表。您的代码产生类似[[[],{Key1,Path1}],{Key2,Path2}] 的结果,而我的版本给出[{Key2,Path2},{Key1,Path1}]

一些备注: 在 erlang 中,if 的使用并不频繁,在我看来主要是因为最后一个 true -&gt; DoSomething() 子句在大多数情况下非常缺乏表达力。 另外一点,在列表中搜索不是很快,隔离搜索不是问题,但在您的情况下,编码和解码功能正在为每个字符执行此操作,在我看来,地图更适合存储编码和解码表而不是键/值列表。

【讨论】:

  • 谢谢你,帕斯卡!我怀疑我可能不小心在 encode_table 中创建了一个嵌套列表,但我不确定,因为如果 Erlang Shell 很长,它不会打印出整个结果。我会纠正的。我还将记住将来向我的函数 cmets 添加有关所需结果的信息,正如您指出的那样,您不知道我所追求的结果。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-10-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多