【问题标题】:Ada How to Get Input Character by CharacterAda 如何逐个字符地获取输入字符
【发布时间】:2012-06-11 00:10:06
【问题描述】:

我是 Ada 的新手,我正在尝试将 Recursive Decent Parser 的编译器教程中的代码转换为 Ada。移植 Jack W Crenshaw 的教程“让我们构建编译器”一直是我学习多种语言的最喜欢的方式。在第三章之前,我使用单字符标记完成了所有工作。向多字符标记的转变一直很麻烦。

我有类似 sudo 代码的代码:

procedure GetName is
  token: Ada.Strings.Unbounded;
begin
  while IsAlNum(Look) loop
    Token := Token & Look;
    GetChar;
  end loop
end GetName;

现在我知道 Ada 打算让字符串保持静态。但我需要能够将从输入中获取的每个新字符连接到 Token 中的字符集合。 Look 是全局预测值(最后输入的字符)。

感谢您的帮助。另外,网上有什么好的 Ada 教程或食谱网站吗?我为 C 程序员阅读了 Lovelace 和 Ada。 Ada RM 有点正式,只显示规格不使用...

再次感谢!

【问题讨论】:

    标签: string ada


    【解决方案1】:

    在这个问题的最后,您似乎在寻求 Ada 字符串处理方面的帮助。

    是的,Ada 字符串确实最好作为静态字符串处理,而不是可调整大小的缓冲区。有三种典型的方法来处理这个问题。

    首先是创建一个非常大的String 缓冲区,并使用一个单独的Natural 变量来保存字符串的逻辑长度。这有点痛苦,而且有点容易出错,但至少比 C 中在缓冲区末尾不断扫描空值的方法快。

    第二个是直接使用 Ada.Strings.Unbounded.Unbounded_String。这是大多数人都会做的事情,因为如果你习惯于以程序化的方式思考事情,这将是最简单的。

    第三个,(如果可能的话,我更喜欢)是处理你的字符串功能。您在这里需要的主要见解是 Ada Strings 确实是静态的,但是您可以控制它们的生命周期,并且如果您进行函数式编程,您可以随时动态地生成静态字符串。

    例如,我可以通过执行以下操作来创建一个新的 Token 字符串,其长度为我想要的任何长度(理论上无限前瞻):

    function Matches_Token (Scanned : String) return boolean;  --// Returns true if the given string is a token
    function Could_Match_Longer (Scanned : String) return boolean; --// Returns true if the given string could be part of a larger token.
    function Get_Next_Char return Character;  --// Returns the next character from the stream
    procedure Unget; --// Puts the last character back onto the stream
    procedure Advance (Amount : Natural); --// Advance the stream pointer the given amount
    function Longest_Matching_Token (Scanned : String) return String is
        New_Token : constant String := Scanned & Get_Next_Char;
    begin
        --// Find the longest token a further scan can match
        if Could_Match_Longer(New_Token) then
            declare 
                LMT : constant String := Longest_Matching_Token (New_Token);
            begin
                if LMT /= "" then
                    unget;
                    return LMT;
                end if;
            end;
        end if;
    
        --// See if this string at least matches.
        if Matches_Token(New_Token) then
            unget;
            return New_Token;
        else
            unget;
            return "";
        end if;
    end Build_Token;
    
    function Get_Next_Token return String is
        Next_Token : constant String := Build_Token("");
    begin
        Advance (Next_Token'length);
        return Next_Token;
    end Get_Next_Token;
    

    这并不总是最有效的字符串处理方法(堆栈使用过多),但通常是最简单的。

    在实践中,扫描和解析实际上是一种特殊情况的应用程序,通常建议避免丑陋的东西,例如缓冲区(方法 1)和 goto。

    【讨论】:

    • 您好,感谢您提供的信息。移植这个编译器一直是我最喜欢的学习新语言的方法之一。它似乎带来了许多在简单应用程序中通常不会出现的问题。它还为我使用语言提供了一个很好的基准。本教程中的编译器适用于 68K 机器,但我过去成功地将其转换为 8051、6811、8086 甚至 C 以用作交叉编译器。现在我正在努力学习 Ada,但实际上只有一手好几个小时的时间来使用它。我发现很难找到信息。
    • @user693336 - 当然,我发现学习语言的最好方法是尝试在其中编写一些东西。字符串的这个问题是它们是任何 Ada 学习者尝试使用的第一件事,而 Ada 数组规则具有微妙(但影响深远)的含义。除非您习惯于函数式语言,否则第一种方法通常是尝试使用 C 字符串处理方法,这总是会导致巨大的挫败感(除非您放弃并使用 Ada.Strings.Unbounded)。有点不幸,艾达最奇怪的傻瓜是大多数菜鸟尝试做的第一件事,但它确实存在。
    【解决方案2】:

    Ada 的单字符“get”方法是Ada.Text_IO.Get。在 Text_IO 包的那个部分中还有 Look_Ahead 和 Get_Immediate 过程。

    Rosetta Code 的 Ada Category 是很多 Ada 示例的好来源。

    【讨论】:

    • 我已经可以得到一个字符了。我的问题是修改现有代码以接受多字符输入和令牌。我目前正在使用 Ada.Text_IO.Get,它适用于单个字符。我发现在 Ada 中从单字符标记更改为多字符标记需要做很多工作,而在 C/C++、Pascal、PHP、Python 中……这是一个简单的步骤。但是 Ada 的严格类型检查意味着更改会在整个代码中传播。但我想这就是这个练习的重点,学习一门新语言。
    • @user693336 - 这是关于 Ada 的一个非常基本的事情(这常常让 C 用户感到沮丧)。您仍然可以在 Ada 中“破解”,但是当您进行基本对象角色更改时,强大的类型系统会迫使您停止并正确重新设计事物。这实际上是该语言的优点之一。
    • 刚刚看了 OpenToken。一旦我得到几个星期的 Ada,我将不得不玩这个!太好听了!!!
    【解决方案3】:

    如果您要使用 Ada 2005 或更高版本(实际上 95 可能有,但不完全确定),您可以使用 Streams。像这样的:

    With Ada.Text_IO;
    
    With
    Ada.Streams.Stream_IO,
    Ada.Text_IO.Text_Streams,
    Ada.IO_Exceptions;
    
    
    Procedure IO is
        Use Ada.Text_IO;
    
        -- Get the Standard_Input.
        Input_File : Ada.Text_IO.File_Type:= Ada.Text_IO.Standard_Input;
    
        -- Create a stream from the Standard Input.
        Input_Stream : Access Ada.Streams.Root_Stream_Type'Class:=
          Ada.Text_IO.Text_Streams.Stream( File => Input_File );
    
    Begin
    
        GET_USER_INPUT:
        declare
        C: Character;
        begin
        loop
            Character'Read( Input_Stream, C );
            exit when C = '*';
            -- Build your string here.
        end loop;
        -- THIS IS AN ALTERNATE WAY FOR EXITING THE ABOVE LOOP.
        Exception
        When ADA.IO_EXCEPTIONS.END_ERROR => Null; -- Raised normally at EOF.
        end GET_USER_INPUT;
        -- Suggested, refactoring GET_USER_INPUT into a function.
    
        Put_Line( "Testing." );
    
    End IO;
    

    【讨论】:

    • 谢谢,我还发现稍微改变我的想法并定义一个字符数组并强制限制输入长度给了我一个可行的解决方案。但是,字符数组的使用在其他程序中引起了问题。 Ada 与 C/C++ 的不同之处在于它强制类型和数据大小。
    • 我知道;我真的很喜欢它——当然,它有时会有点挑战,但它也确保我不必调试神秘和间歇性故障的程序。
    • 作为最初编写 OpenToken(Ada 编译器构建工具包)的人,为了易用性,我倾向于使用流输入而不是 Text_IO。但是,如果您想真正使用您的这个编译器,您可能会发现编写一个小包/类以将尽可能多的文件读取到 RAM 中,然后从中获取字符会很有帮助之后缓冲。你做的 I/O 越少越好。
    • 这里的目的不是为了实际使用而构建编译器。我已经很多年没有在任何东西上使用过 68K... 目的是尝试复制 Jack 的原始编译器结构大纲,这样我就可以学习 Ada 并将其与其他语言的其他实现进行比较。在过去的几年里,我用 PHP、Python、Perl、Free Pascal C、C++、Scheme、Java、Gambas 和其他语言编写了同样的编译器。所以它只需要工作而不是作为生产编译器。我会使用 Flex/Bison 作为真正的编译器。嘿,有 Ada 版本的 Flex/Bison 吗?
    • ics.uci.edu/~self 你可以看到Aflex 和Ayacc 的三个链接;在sourceforge.net/projects/gnavi GWenerator 下也应该有 Flex/Yacc -- Dmitri 在这里有一个解析器包:dmitry-kazakov.de/ada/components.htm#Parsers_etc -- 最后是 OpenToken 在这里:stephe-leake.org/ada/opentoken.html
    【解决方案4】:

    我编写了一个函数来连接从输入中提取的每个新字符,并将其全部作为字符串返回。它可能会适应您的需要。

    FUNCTION get_a_string ( ch : IN Character ) RETURN String IS
    -----------------------------------------------------------------
    --| Recursively hack out a string from a stream of single
    --| character input. Starting with an ESC sentinel and ending
    --| with an EOL sentinel.
    --| ESC is the ESC character and EOL is a space.
    ----------------------------------------------------------------
        next : Character;
        ch2s : String(1..1);
    BEGIN -- get_a_string
        Ada.Text_IO.Put( "Waiting: ");--BARF
        Ada.Text_IO.Get( Item => next );
    
        IF ch = ESC THEN -- start
           RETURN get_a_string(next);
        ELSIF next = EOL THEN --Escape Case
           ch2s(1) := ch;
           RETURN ch2s;
        ELSE -- Keep getting input
           RETURN ch & get_a_string(next);
        END IF;
    END get_a_string;
    

    万一我的导师或助教发现这与我上交的代码匹配。我写了它,所以我没有作弊。

    【讨论】:

      猜你喜欢
      • 2010-10-23
      • 1970-01-01
      • 2013-10-20
      • 2013-08-21
      • 1970-01-01
      • 2013-09-17
      • 2014-04-02
      • 2013-01-18
      • 1970-01-01
      相关资源
      最近更新 更多